execute, reach the web through web_search and fetch_url, delegate to inline and async sub-agents, and extend itself only from operator-controlled sources — MCP servers, skills, hooks, sandbox backends and AGENTS.md memory. Its sole approval authority is the local developer or CI identity that launched it, and its outbound counterparty set is an operator-configured closure. The defining obligations are that every side-effecting action requires human approval — with headless auto-execution permitted only under an explicit, operator-scoped shell allow-list — and that content arriving from tool results, fetched pages or committed project files is treated as data, never as instructions.The dominant pattern is a safety model that exists almost entirely on the interactive path and thins to nothing on the headless one. In the TUI the approval gate is real and well built — every side-effecting tool is registered in agent._add_interrupt_on, Auto mode's classifier fails closed to human review, and tools.py carries a genuine SSRF guard with DNS pinning — but client/non_interactive.py resolves every non-shell action to {"type": "approve"}, and the one control that does apply to shell, config.is_shell_command_allowed, inspects only the first token of each command segment and skips every check under the all sentinel.
Those two gaps meet an ingress the project has documented but not closed: local_context.build_detect_script appends the working directory's Makefile and file listing to the system prompt on every turn, and tool results re-enter context with no provenance labeling — so a hostile repository can steer a headless run into file writes, outbound fetches and allow-listed interpreter execution with no human in the loop.
libs/code/deepagents_code) built on the deepagents SDK and LangGraph, shipping a Textual TUI and a headless runner that both drive an ephemeral langgraph dev subprocess over HTTP+SSE on loopback. Approval is implemented as a LangGraph interrupt map in agent._add_interrupt_on covering execute, write_file, edit_file, delete, web_search, fetch_url, task and the async sub-agent tools, with three approval modes (Manual, Auto, YOLO) in approval_mode.py and a classifier-backed Auto policy in auto_mode.py. Notable external surface: an unauthenticated local agent API (LANGGRAPH_AUTH_TYPE=noop), MCP stdio and remote servers loaded from a repo-committed .mcp.json behind a fingerprint-bound trust prompt, and an SSRF-guarded fetch path in tools.py. Two divergences from the remit's shape are visible at the structural level: the headless runner in client/non_interactive.py resolves every non-shell tool to an unconditional approval, and the http_request tool the remit and threat model both name no longer exists in the code.Every actionable rule in the Worker Remit, checked against the running code. Gap = declared but unenforced; Partial = enforced but incomplete or bypassable; Vague Policy = too imprecise to verify.
| Rule ID | Section | Rule (quoted) | Status | Finding |
|---|---|---|---|---|
| R-01 | Prohibited Behaviors | "MUST NOT treat content that arrives from tool results or retrieved sources — fetched web pages, `web_search` snippets, MCP tool responses, file and project contents (including `Makefile`, `.env`, committed config), sub-agent output, or memory/skill files — as authoritative instructions that redefine the agent's goals, expand its scope, or override its approval gates." | Partial | PRAX-2026-08-12-004 |
| R-02 | Prohibited Behaviors | "MUST NOT redefine, expand, or remove its own operating objectives, authorized scope, or safety gates on its own initiative." | Partial | PRAX-2026-08-12-001 |
| R-03 | Prohibited Behaviors | "MUST NOT act outside the operator's requested task and project scope — e.g. modifying files, systems, or accounts unrelated to the task, or pursuing goals the operator did not assign." | Partial | PRAX-2026-08-12-002 |
| R-04 | Approved Communication Channels | "Approved Communication Channels" | Verified | — |
| R-05 | Approved Communication Channels | "MUST bind a loopback interface only and MUST NOT expose the agent-runtime API to non-loopback interfaces or any non-local network." | Verified | — |
| R-06 | Authorized Counterparties — Trusted People / Accounts | "Trusted People / Accounts" | Verified | — |
| R-07 | Authorized Counterparties — Trusted Domains | "Trusted Domains" | Partial | PRAX-2026-08-12-011 |
| R-08 | Authorized Counterparties — Trusted Services / Integrations | "Trusted Services / Integrations" | Verified | — |
| R-09 | Authorized Counterparties — Explicitly Forbidden | "MUST NOT initiate connections to, or send data to, any network endpoint derived from retrieved content, tool output, or LLM-generated arguments rather than from operator configuration." | Partial | PRAX-2026-08-12-002 |
| R-10 | Tools and Capabilities | "Allowed Tools (Known Good Baseline)" | Verified | — |
| R-11 | Tools and Capabilities | "Only capabilities in this baseline are authorized; the agent MUST NOT acquire, load, or expose tools or capabilities beyond it in response to LLM output, retrieved content, or repository-committed configuration." | Partial | PRAX-2026-08-12-007 |
| R-12 | Data Boundaries — Allowed Data Sources | "Allowed Data Sources" | Verified | — |
| R-13 | Data Boundaries — Forbidden Data Movement | "Credentials and secrets (provider API keys, tokens, passwords, environment secrets) MUST NOT be transmitted to any external destination, nor persisted into memory files, skills, session/checkpoint stores, or logs." | Partial | PRAX-2026-08-12-010 |
| R-14 | Data Boundaries — Forbidden Data Movement | "The secret-bearing process environment MUST NOT be forwarded wholesale to spawned subprocesses (MCP servers, hooks, the runtime server, sandbox setup) — each child MUST receive only the environment it requires." | Partial | PRAX-2026-08-12-006 |
| R-15 | Data Boundaries — Forbidden Data Movement | "Persisted conversation and session data MUST be protected at rest commensurate with its sensitivity (e.g. access-restricted file permissions or encryption at rest)." | Gap | PRAX-2026-08-12-010 |
| R-16 | Data Boundaries — Forbidden Data Movement | "Conversation or project data MUST NOT be sent to third-party, sandbox, or provider destinations that retain it without the operator's awareness and opt-in." | Verified | — |
| R-17 | Data Boundaries — Forbidden Data Movement | "Persisted conversation/session data and memory MUST be retained only per the operator-configured retention/purge policy, and MUST NOT be kept indefinitely by default." | Gap | PRAX-2026-08-12-010 |
| R-18 | Action Boundaries — Allowed Without Approval | "Allowed Without Approval" | Partial | PRAX-2026-08-12-002 |
| R-19 | Action Boundaries — Requires Human Approval Before Execution | "Shell / command execution MUST require explicit human approval before the command runs." | Partial | PRAX-2026-08-12-003 |
| R-20 | Action Boundaries — Requires Human Approval Before Execution | "Headless auto-execution without that approval is permitted ONLY under an explicit, operator-scoped shell allow-list." | Verified | — |
| R-21 | Action Boundaries — Requires Human Approval Before Execution | "An "allow everything" allow-list setting is out of bounds." | Gap | PRAX-2026-08-12-003 |
| R-22 | Action Boundaries — Requires Human Approval Before Execution | "Absent an allow-list, approval is required in every execution context, including non-interactive and headless/CI runs." | Gap | PRAX-2026-08-12-002 |
| R-23 | Action Boundaries — Requires Human Approval Before Execution | "Approval MUST NOT be silently bypassed." | Partial | PRAX-2026-08-12-007 |
| R-24 | Action Boundaries — Requires Human Approval Before Execution | "Creating, writing, editing, or deleting files MUST require human approval." | Partial | PRAX-2026-08-12-002 |
| R-25 | Action Boundaries — Requires Human Approval Before Execution | "Outbound web actions — `fetch_url`, `http_request`, `web_search` — MUST require human approval before the request is made." | Partial | PRAX-2026-08-12-002 |
| R-26 | Action Boundaries — Requires Human Approval Before Execution | "Delegating to a sub-agent (`task`) and launching, updating, or cancelling an async sub-agent MUST require human approval." | Partial | PRAX-2026-08-12-002 |
| R-27 | Action Boundaries — Requires Human Approval Before Execution | "Loading a project-level (repository-supplied) MCP server MUST require explicit operator trust approval before the server is spawned or connected." | Verified | — |
| R-28 | Action Boundaries — Never Allowed | "The agent MUST NOT disable, weaken, or bypass the human-approval gate on side-effecting tools on its own initiative or in response to retrieved/tool content." | Partial | PRAX-2026-08-12-007 |
| R-29 | Action Boundaries — Never Allowed | "Destructive filesystem or shell operations MUST NOT execute without either human approval or execution inside an isolated sandbox backend." | Gap | PRAX-2026-08-12-002 |
| R-30 | Action Boundaries — Never Allowed | "Untrusted project content (`Makefile`, `.env`, committed config) MUST NOT drive a side-effecting action without passing the human-approval gate; an isolated sandbox backend is the recommended posture for untrusted repositories, but running on the host with the approval gate in force is authorized (this agent is host-resident by design, like comparable CLI coding assistants)." | Gap | PRAX-2026-08-12-001 |
| R-31 | Action Boundaries — Never Allowed | "The agent MUST stop at the operator-configured autonomy budgets and MUST NOT continue past them: the maximum autonomous turns (`--max-turns`), the task timeout (`--timeout`), or the transient-retry ceiling." | Verified | — |
| R-32 | Action Boundaries — Never Allowed | "Loading configuration, model-provider definitions, or skill/memory definitions MUST NOT cause arbitrary code to execute before those definitions have been validated." | Partial | PRAX-2026-08-12-008 |
| R-33 | Escalation Rules — Halt Agent and Alert Operator | "Halt and alert the operator if retrieved content, tool output, or configuration attempts to make the agent disable its approval gate, exfiltrate credentials, or execute code outside an isolated sandbox against untrusted input." | Gap | PRAX-2026-08-12-015 |
| R-34 | Escalation Rules — Alert Operator (Do Not Halt) | "When a tool call's arguments contain hidden/dangerous Unicode or a mixed-script / homoglyph-spoofed URL, surface a clear warning in the approval dialog before the operator approves." | Verified | — |
| R-35 | Escalation Rules — Alert Operator (Do Not Halt) | "When a previously-trusted project MCP configuration's fingerprint changes, re-prompt the operator for trust rather than loading the changed servers silently." | Verified | — |
| R-36 | Escalation Rules — Log Only | "All side-effecting tool executions and approval decisions MUST be recorded to a durable, structured audit record." | Gap | PRAX-2026-08-12-009 |
Findings, ordered by severity — each linked to its remit rule, evidence, and a recommended action. Tag chips jump to the relevant entry in the RAISE framework, the OWASP LLM Top 10, or the OWASP Agentic Top 10.
LLM06 primary — the finding's main OWASP categoryASI10 secondary — a category it also touches
CRITICAL PRAX-2026-08-12-001 Hostile repository content reaches the model as system-prompt text and can drive file writes and shell execution with no human in the loop under headless runs.
"Untrusted project content (`Makefile`, `.env`, committed config) MUST NOT drive a side-effecting action without passing the human-approval gate; an isolated sandbox backend is the recommended posture for untrusted repositories, but running on the host with the approval gate in force is authorized (this agent is host-resident by design, like comparable CLI coding assistants). / MUST NOT redefine, expand, or remove its own operating objectives, authorized scope, or safety gates on its own initiative."
- Label untrusted spans before they enter the prompt: wrap the LocalContextMiddleware detect-script output and every ToolMessage body in an explicit untrusted-content delimiter in
local_context._get_modified_request, and stop appending Makefile contents by default. - Require the sandbox backend (or an explicit operator acknowledgement per run) whenever headless mode is combined with a shell allow-list, so untrusted project content cannot reach host execution unattended.
CRITICAL PRAX-2026-08-12-002 Headless mode approves every non-shell side-effecting tool unconditionally — file writes, deletes, web fetches and sub-agent delegation all run with no gate.
"Absent an allow-list, approval is required in every execution context, including non-interactive and headless/CI runs. / Creating, writing, editing, or deleting files MUST require human approval. / Outbound web actions — `fetch_url`, `http_request`, `web_search` — MUST require human approval before the request is made. / Delegating to a sub-agent (`task`) and launching, updating, or cancelling an async sub-agent MUST require human approval. / Destructive filesystem or shell operations MUST NOT execute without either human approval or execution inside an isolated sandbox backend. / Allowed Without Approval / MUST NOT act outside the operator's requested task and project scope — e.g. modifying files, systems, or accounts unrelated to the task, or pursuing goals the operator did not assign. / MUST NOT initiate connections to, or send data to, any network endpoint derived from retrieved content, tool output, or LLM-generated arguments rather than from operator configuration."
- Extend the headless decision handler in
client/non_interactive.pyso file-mutating, outbound and delegation tools are rejected by default and admitted only through explicit per-tool operator allow-lists, mirroring the shell path rather than bypassing it. - Until that lands, make the unrestricted posture visible and deliberate: require an explicit acknowledgement flag before a headless run may execute
write_file,edit_file,deleteor the async sub-agent tools.
CRITICAL PRAX-2026-08-12-003 The shell allow-list is not an execution boundary — the `all` sentinel skips every check, and normal lists validate only each segment's first token.
"An "allow everything" allow-list setting is out of bounds. / Shell / command execution MUST require explicit human approval before the command runs."
- Remove the
allsentinel fromparse_shell_allow_list, or gate it behind the same versioned acknowledgement that YOLO mode requires, so the out-of-bounds setting cannot be reached by a flag alone. - Document the allow-list in
ui.show_help()and the help screen as an ergonomic auto-approve heuristic rather than a security control, and reject known interpreter and wrapper names from allow-lists unless the operator passes an explicit override.
HIGH PRAX-2026-08-12-004 Untrusted content enters the model context with no provenance labeling — tool results verbatim, and working-directory files appended to the system prompt each turn.
"MUST NOT treat content that arrives from tool results or retrieved sources — fetched web pages, `web_search` snippets, MCP tool responses, file and project contents (including `Makefile`, `.env`, committed config), sub-agent output, or memory/skill files — as authoritative instructions that redefine the agent's goals, expand its scope, or override its approval gates."
- Introduce a provenance wrapper for untrusted spans and apply it to the LocalContextMiddleware output and to every ToolMessage body, so the model receives an explicit trusted/untrusted boundary rather than undifferentiated text.
- Make the Makefile section of
build_detect_scriptopt-in, and strip instruction-shaped lines from what it emits.
HIGH PRAX-2026-08-12-005 The local agent-runtime API runs with authentication disabled, so any same-user process that finds the port can read thread state or inject messages.
- Generate a per-session bearer token in
ServerProcess.start, pass it to the server and require it inRemoteAgent's requests — the token never leaves the process pair, so it costs the user nothing while closing the same-user injection path. - If the noop default is retained, narrow the exposure by binding a Unix domain socket instead of a TCP loopback port where the platform allows it.
HIGH PRAX-2026-08-12-006 The full process environment, including provider API keys, is copied into the runtime server subprocess, and MCP stdio servers receive an unfiltered env dict.
"The secret-bearing process environment MUST NOT be forwarded wholesale to spawned subprocesses (MCP servers, hooks, the runtime server, sandbox setup) — each child MUST receive only the environment it requires."
- Build the server subprocess environment from an explicit allowlist of the variables the server actually needs, and pass the selected provider credential rather than inheriting every key, reusing
config_manifest._is_secret_envas the filter basis. - Reject or warn on interpreter-hijacking keys (PATH, LD_PRELOAD, DYLD_*, PYTHONPATH, NODE_OPTIONS) in
mcp_tools._validate_server_config's env handling.
HIGH PRAX-2026-08-12-007 MCP tools exempt themselves from the approval gate through a server-declared readOnlyHint, so a trusted server decides which of its own tools need review.
"Only capabilities in this baseline are authorized; the agent MUST NOT acquire, load, or expose tools or capabilities beyond it in response to LLM output, retrieved content, or repository-committed configuration. / Approval MUST NOT be silently bypassed. / The agent MUST NOT disable, weaken, or bypass the human-approval gate on side-effecting tools on its own initiative or in response to retrieved/tool content."
- Treat the read-only annotation as a display hint rather than a gate decision: keep every MCP tool in the interrupt map by default and let the operator opt specific named tools out through
[mcp]configuration they control. - Record the tool-definition fingerprint alongside the existing server-definition fingerprint in
model_config's approval store, and re-prompt when an approved server's tool set or annotations change.
HIGH PRAX-2026-08-12-008 A configured `class_path` provider imports its module before any validation, so module-level code executes ahead of the BaseChatModel type check.
"Loading configuration, model-provider definitions, or skill/memory definitions MUST NOT cause arbitrary code to execute before those definitions have been validated."
- Resolve the provider class through an explicit registry of known module paths, or verify the target against a declared entry-point group, so an arbitrary module path is never imported purely because it appeared in
config.toml. - Surface the configured
class_pathto the operator on first use with a one-time confirmation, matching the trust prompt already used for project MCP servers.
HIGH PRAX-2026-08-12-009 No durable structured record of side-effecting tool executions or approval decisions exists by default — every audit surface is opt-in or in-memory.
"All side-effecting tool executions and approval decisions MUST be recorded to a durable, structured audit record."
- Write an always-on append-only JSONL action log under the state directory recording tool name, arguments digest, approval decision, decision source (manual, auto-classifier, allow-list, YOLO) and outcome, reusing the redaction already implemented in
hooks/transcript.py. - Make the headless auto-approval decisions in
_make_hitl_decisionemit to that log rather than to console output alone, so a CI run leaves an artifact that can be reviewed after the fact.
MEDIUM PRAX-2026-08-12-010 Conversation checkpoints persist unencrypted and indefinitely, with no retention or purge policy and no explicit mode on the state directory.
"Persisted conversation and session data MUST be protected at rest commensurate with its sensitivity (e.g. access-restricted file permissions or encryption at rest). / Persisted conversation/session data and memory MUST be retained only per the operator-configured retention/purge policy, and MUST NOT be kept indefinitely by default. / Credentials and secrets (provider API keys, tokens, passwords, environment secrets) MUST NOT be transmitted to any external destination, nor persisted into memory files, skills, session/checkpoint stores, or logs."
- Create the state directory and the checkpoint database with 0700/0600 modes explicitly in
sessions.get_db_path, matching whatapproval_modeandhooks/transcriptalready do for their own files. - Add an operator-configurable retention window under
[sessions]and prune expired threads (and their offloaded history) on startup, so the default is bounded rather than indefinite.
MEDIUM PRAX-2026-08-12-011 Auto-update is enabled by default and replaces the runtime from PyPI with no signature verification, an egress destination outside the operator-configured trust closure.
"Trusted Domains"
- Verify the downloaded distribution against a pinned hash or a signature before the replacement install, applying the pattern already used by
managed_tools.ensure_ripgrep. - Name PyPI and github.com as framework egress destinations in the package documentation so operators auditing outbound traffic can reconcile them against their trust closure.
MEDIUM PRAX-2026-08-12-012 No component inventory ships with the package, and one dependency carries no upper bound, so exposure cannot be assessed when an advisory lands.
- Generate a CycloneDX SBOM as a release artifact from the committed
uv.lockand attach it to the published distribution, so a future advisory can be matched against a shipped inventory. - Add an upper bound to the
langsmith[sandbox]requirement to match the convention the rest of the dependency list follows.
MEDIUM PRAX-2026-08-12-013 The threat model has drifted from the code — it documents a built-in `http_request` tool that no longer exists and dismisses an SSRF risk the code now actually guards.
- Refresh the threat model's component, data-flow and dismissal sections against the current source, removing
http_requestand re-stating D3 to credit the SSRF guard and DNS pinning that now exist. - Tie regeneration to a release check so the document cannot silently age past the code it describes.
MEDIUM PRAX-2026-08-12-014 Sub-agent definitions discovered from project directories become system prompts verbatim, with no content validation on the body text.
- Apply the same trust prompt that project MCP servers receive to project-supplied sub-agent definitions, keyed to the project root and a fingerprint of the definition body.
- Reject sub-agent bodies that attempt to grant tools or approval bypasses beyond the parent agent's configured set, rather than passing the text through unread.
MEDIUM PRAX-2026-08-12-015 No halt-and-alert path exists for content attempting to disable the approval gate or exfiltrate credentials — the only detector is opt-in and interactive-only.
"Halt and alert the operator if retrieved content, tool output, or configuration attempts to make the agent disable its approval gate, exfiltrate credentials, or execute code outside an isolated sandbox against untrusted input."
- Promote the classifier's denial categories into a mode-independent escalation check that halts the run and writes an alert record when a tool call matches, rather than leaving detection tied to Auto mode selection.
- Give headless runs a non-zero exit code distinct from the turn-budget code when such a condition fires, so CI can tell a refused run from an exhausted one.
Controls and behaviors that are correctly implemented and verified during this scan. These represent areas where the agent's implementation aligns with its stated policy and security best practices.
Approval gate covers every side-effecting tool in interactive mode
The LangGraph interrupt map registers execute, write_file, edit_file, delete, web_search, fetch_url, task and the three async sub-agent tools, plus every MCP tool lacking a read-only annotation, so the default interactive posture is approval-required.
Auto mode fails closed to human review
When the approval classifier errors, times out or the control-state record is unreadable, the middleware routes the call to a human rather than allowing it, and repeated unavailability trips a documented fallback.
SSRF guard with DNS-rebinding protection on the fetch path
Every fetch hop is scheme-checked and resolved, private, loopback, link-local and IMDS addresses are rejected including IPv4-mapped and 6to4 wrappers, and the connection is pinned to the vetted IP so re-resolution cannot move the target.
Hook subprocesses receive a secret-stripped environment
sanitize_hook_environ filters every variable whose name looks like a credential before a hook command is spawned, so user-authored hooks do not get ambient access to provider API keys.
Project MCP trust is bound to a server-definition fingerprint
Remembered approvals record the project root, server name and a fingerprint of the server definition, so a changed command, URL or transport under the same name forces a fresh operator decision instead of loading silently.
Skill trust store enforces containment and re-verifies on load
A SKILL.md resolving outside every trusted root is refused, one-time approvals are keyed to the resolved target directory, and a stored entry that no longer resolves to itself is dropped rather than followed.
Transcript projections are written privately and redacted
Hook-readable conversation transcripts are written with 0600 file and 0700 directory modes and pass through credential-assignment, bearer-token and prefixed-token redaction before they reach disk.
Machine-managed memory block is protected from agent edits
A middleware intercepts write_file, edit_file and delete calls against the guarded AGENTS.md region, restores the managed block and returns an error, failing closed when a guarded file cannot be read.
Autonomy turn budget is enforced with a distinguishable exit code
The headless loop counts agentic turns against --max-turns (or an internal default of 50) and aborts with exit code 124 rather than continuing, so a stuck retry loop terminates.
Managed ripgrep binary is verified against a pinned checksum
When rg is absent the pinned release tarball is fetched and checked against an in-tree SHA-256 table before an atomic move into place, and a mismatch aborts the install.
Log files found in the agent's workspace during this scan. Reviewing these files provides runtime evidence to complement the static analysis above.
| Path | Source | Content Type | Purpose | Last Modified | Status |
|---|---|---|---|---|---|
| ~/.deepagents/.state/ (hook transcript JSONL projections, per thread and per subagent) | deepagents_code/hooks/transcript.py | versioned JSONL records with secret redaction | Conversation transcript made available to hook commands via transcript_path / agent_transcript_path; 20-revision backup pruning | unknown | Inferred |
| DEEPAGENTS_CODE_DEBUG_FILE (default debug log under the dcode state directory) | deepagents_code/__init__.py configure_debug_logging | plaintext Python logging records | Package-wide debug logging; file handler attaches only when DEEPAGENTS_CODE_DEBUG is truthy | unknown | Inferred |
| ~/.deepagents/.state/sessions.db | deepagents_code/sessions.py get_db_path | SQLite LangGraph checkpoint store | Full conversation state including tool arguments and results; state rather than an audit log, and the only always-on durable record | unknown | Inferred |
Each card represents one category and shows the top 3 findings. All items in the Findings section.
Each card represents one category and shows the top 3 findings. All items in the Findings section.
Overall maturity assessment across the six categories of the RAISE framework. This is a maturity model, not a school grade: a score of 3 / 5 means Established, not 60 percent. Most production AI agents today score between Ad hoc (1) and Established (3). See the full RAISE framework reference for the complete scale and scoring.
http_request has been removed entirely) and scope is framed in system_prompt.md, but nothing in the shipped default confines the agent to the task or project — the only code-level containment, auto_mode._command_paths_stay_in_worktree and _repository_bounds.py, applies to opt-in Auto mode and the rubric sub-agent, so the dominant path is covered by prompt-level instruction alone.execute stdout re-enter the model context verbatim with no provenance labeling or injection scanning, and local_context._section_makefile appends unsanitized working-directory content to the system prompt every turn; the markdownify conversion and script-tag suppression in tools.py reduce noise rather than establish trust.agent._add_interrupt_on gates every side-effecting tool in interactive mode, Auto mode fails closed to human review, and the fetch path is SSRF-guarded — but coverage is incomplete in ways that are structural rather than incidental: client/non_interactive.py approves every non-shell action unconditionally, config.is_shell_command_allowed checks only tokens[0], the local agent API runs with LANGGRAPH_AUTH_TYPE=noop, and MCP tools self-exempt from the gate via a server-declared readOnlyHint.uv.lock, an exact SDK pin (deepagents==0.7.0b2), upper bounds on nearly every dependency, monthly Dependabot across uv and github-actions, dedicated check_lockfiles / check_dep_freshness / check_sdk_pin workflows, and a SHA-256-verified managed ripgrep binary — against no SBOM or component inventory (M10: none), no dependency or container scanner (M11: Dependabot only), and a default-on auto-update that installs from PyPI unverified.THREAT_MODEL.md with thirteen threats traced to code references, M5 records eight dated revisions, and M9 shows that analysis driving the project's own defences (the 2026-06-25 entry adds T12 and the dotenv BASH_ENV/ENV denylist together). Both Ad hoc and Partial are defensible on that evidence; Partial is taken because the feedback loop is documented and ongoing rather than one-off, and the security-adjacent unit tests of M1 run under make test (M6).~/.deepagents/hooks.json, file logging is gated behind DEEPAGENTS_CODE_DEBUG, and the always-on surface is a bounded in-memory ring buffer feeding a debug console.Maturity Scoring Rubric
Every score above is based on this scale. A score is a snapshot of observable posture — not a verdict on the people or team behind the system.
| Score | Label | Meaning |
|---|---|---|
| 5 | Exemplary | Best-in-class; automated, continuously tested, reference quality. Rarely achieved in shipping systems. |
| 4 | Strong | Comprehensive controls, active management, minor gaps. Production-ready. |
| 3 | Established | Documented controls consistently applied; known gaps accepted. A respectable baseline. |
| 2 | Partial | Some controls exist but coverage is incomplete; key gaps remain. |
| 1 | Ad hoc | Informal or inconsistent measures; relies on individual judgment. |
| 0 | Absent | No evidence this category is addressed at all. |