Skip to content

feat(rules): consolidate the 36 open rule pack pull requests - #151

Merged
sairenchristianbuerano merged 36 commits into
mainfrom
recover/rule-packs
Sep 21, 2026
Merged

sairenchristianbuerano merged 36 commits into
mainfrom
recover/rule-packs

Conversation

@sairenchristianbuerano

Copy link
Copy Markdown
Collaborator

Consolidates 36 of @bradAGI's open rule pull requests into one branch so they can merge together with their engine fixtures.

Why this is one pull request

@bradAGI deleted the bradAGI/trustabl fork, which disables GitHub's Reopen button on all 36 engine pull requests. The commits survive in this repository's own refs/pull/<n>/head, so every one was recovered unchanged and is still authored by @bradAGI — nothing was rewritten or re-done.

They are consolidated into a single branch rather than 36 because rules-sync fails in both directions — a rule with no fixture and a fixture with no rule — so 36 separate pairs means 36 chances to leave main red. Two pull requests merged back to back means one short window.

Verified

  • 36 commits, all authored by @bradAGI
  • no duplicate rule ids
  • trustabl rules validate: 275 rules valid under schema 16, up from 228
  • go build ./... clean, gofmt clean
  • go test ./internal/rules/: 681 policy cases pass, 0 fail
  • check-rules-sync.sh clean between the two branches

Merge order

Merge trustabl/agent-reliability-analyzer#218 and this one back to back.

Closes these once merged

The 36 pull requests below are contained in this branch, commit for commit, and should be closed when this merges:

rules PR engine PR rules
#53 trustabl/agent-reliability-analyzer#128 add CREW-007, tool network call has no timeout
#55 trustabl/agent-reliability-analyzer#132 add LC-008, tool raises without a structured error c
#56 trustabl/agent-reliability-analyzer#133 add CREW-008, tool raises without a structured error
#57 trustabl/agent-reliability-analyzer#134 add AG2-013, tool raises without a structured error
#58 trustabl/agent-reliability-analyzer#136 add PYD-009, path parameter used in I/O without vali
#60 trustabl/agent-reliability-analyzer#139 add CREW-009, path parameter used in I/O without val
#61 trustabl/agent-reliability-analyzer#140 add AG2-014, path parameter used in I/O without vali
#62 trustabl/agent-reliability-analyzer#141 add LC-017, mutating tool has no idempotency key
#64 trustabl/agent-reliability-analyzer#143 add PYD-010, PYD-011 tool description quality rules
#66 trustabl/agent-reliability-analyzer#145 add LC-018, LC-019 tool description quality rules
#67 trustabl/agent-reliability-analyzer#146 add CREW-010, CREW-011 tool description quality rule
#68 trustabl/agent-reliability-analyzer#147 add AG2-016, AG2-017 tool description quality rules
#70 trustabl/agent-reliability-analyzer#149 add VAI-015, tool writes to the filesystem
#71 trustabl/agent-reliability-analyzer#150 add PYD-012, tool prints to stdout for diagnostics
#72 trustabl/agent-reliability-analyzer#151 add LC-020, tool prints to stdout for diagnostics
#73 trustabl/agent-reliability-analyzer#152 add CREW-012, tool prints to stdout for diagnostics
#74 trustabl/agent-reliability-analyzer#153 add AG2-018, tool prints to stdout for diagnostics
#82 trustabl/agent-reliability-analyzer#156 add MCP-023, tool prints to stdout, corrupting a std
#83 trustabl/agent-reliability-analyzer#157 add CSDK-019, tool prints to stdout for diagnostics
#84 trustabl/agent-reliability-analyzer#158 add CSDK-020, TypeScript tool HTTP call has no timeo
#85 trustabl/agent-reliability-analyzer#159 add MCP-024, TypeScript tool HTTP call has no timeou
#86 trustabl/agent-reliability-analyzer#160 add ADK-114, TypeScript FunctionTool HTTP call has n
#87 trustabl/agent-reliability-analyzer#164 add OAI-025, OAI-026 tool description quality rules
#88 trustabl/agent-reliability-analyzer#165 add MCP-025, MCP-026 tool description quality rules
#89 trustabl/agent-reliability-analyzer#166 add ADK-115, ADK-116 tool description quality rules
#90 trustabl/agent-reliability-analyzer#167 add CSDK-021, CSDK-022 TypeScript description qualit
#91 trustabl/agent-reliability-analyzer#168 add MCP-027, MCP-028 TypeScript description quality
#92 trustabl/agent-reliability-analyzer#170 add OAI-027, OAI-028 TypeScript description quality
#93 trustabl/agent-reliability-analyzer#171 add LC-021, LC-022 TypeScript description quality ru
#94 trustabl/agent-reliability-analyzer#172 add MCP-029, TypeScript tool writes to the filesyste
#95 trustabl/agent-reliability-analyzer#173 add OAI-029, TypeScript tool writes to the filesyste
#96 trustabl/agent-reliability-analyzer#174 add ADK-117, TypeScript FunctionTool writes to the f
#104 trustabl/agent-reliability-analyzer#176 add LC-024, ambiguous tool name
#105 trustabl/agent-reliability-analyzer#177 add CREW-013, ambiguous tool name
#106 trustabl/agent-reliability-analyzer#178 add AG2-019, ambiguous tool name
#107 trustabl/agent-reliability-analyzer#179 add PYD-013, ambiguous tool name

CrewAI had no network-timeout rule, unlike Pydantic AI (PYD-006),
AutoGen (AG2-012), and the Vercel AI SDK (VAI-011). The blast radius is
larger here than in a single-agent SDK: a crew runs tasks in sequence and
threads each task's output forward, so one stalled tool call blocks every
task behind it. CREW-110's max_iter bounds reasoning steps, not the
wall-clock of one tool call, so it never breaks the stall.

Uses the same call_without_kwarg callee list as PYD-006.
…ontract

LangChain had no error-contract rule; Claude SDK (CSDK-005), OpenAI
(OAI-008), ADK (ADK-005), and MCP (MCP-006) all ship one.

LangChain's default handle_tool_error=False lets a tool exception escape
the AgentExecutor and abort the run, so the model is never told the step
failed. Opting in with handle_tool_error=True only hands the model the
raw str(exception): no failure mode, no retryability signal, and whatever
internal detail the message carried.

Python only. A LangChain.js counterpart is not shippable yet: PredHasRaise
matches the Python grammar node "raise_statement", so has_raise is
structurally always false for a TypeScript tool, whose throws parse as
"throw_statement". A TS error-contract rule needs that engine-side gap
closed first.
…ntract

CrewAI had no error-contract rule; Claude SDK (CSDK-005), OpenAI
(OAI-008), ADK (ADK-005), and MCP (MCP-006) all ship one.

CrewAI stringifies an uncaught tool exception into the agent's
observation, so the agent sees a bare message with no failure mode and no
retryability signal, and usually burns its max_iter budget re-calling the
tool the same way. A crew then threads that unusable output forward as
context to every task sequenced behind it.
…ntract

AutoGen had no error-contract rule; Claude SDK (CSDK-005), OpenAI
(OAI-008), ADK (ADK-005), and MCP (MCP-006) all ship one.

AutoGen runs tools on the executor agent's side and sends the result back
as that agent's reply, so an uncaught exception either aborts
initiate_chat or lands in the message history as a stringified traceback.
In the second case both agents keep conversing with a traceback as the
last observation, and the assistant re-issues the same call until
AG2-004/AG2-006's round caps end the chat. The traceback also persists in
the transcript, carrying internal detail into every later turn's context.
…lidation

Pydantic AI had no path-safety rule; Claude SDK (CSDK-004), OpenAI
(OAI-006), ADK (ADK-004), and MCP (MCP-005) all ship one.

The SDK-specific point is that Pydantic AI's type layer does not cover
this: a parameter annotated str or Path validates cleanly while still
carrying ../../etc/passwd, because the annotation constrains the value's
type and not the region of the filesystem it points at. The fix therefore
pushes containment into the parameter's type via a validator rather than
leaving it per call site.

Uses the per-param call_uses_unnormalized_path_param, matching CSDK-004.
…tion

CrewAI had no path-safety rule; Claude SDK (CSDK-004), OpenAI (OAI-006),
ADK (ADK-004), and MCP (MCP-005) all ship one.

The args_schema does not close the gap: a Pydantic field typed str or
Path validates cleanly while still carrying ../../etc/passwd, because the
schema constrains the argument's type and not the region of the
filesystem it points at. Reach is wider than the owning agent — a crew
shares one process and threads task output forward as context, so what
this tool reads lands in the transcript every downstream task reasons
over, and CREW-104 delegation lets an agent without the tool reach it
through a peer.

Uses the per-param call_uses_unnormalized_path_param, matching CSDK-004.
…tion

AutoGen had no path-safety rule; Claude SDK (CSDK-004), OpenAI (OAI-006),
ADK (ADK-004), and MCP (MCP-005) all ship one.

The AutoGen-specific point is that a registered tool sidesteps the
containment AG2-001 is about. Generated code may run inside a Docker
executor, but a function registered with register_for_execution runs in
the host process with the host's filesystem view regardless of how the
code executor is configured, so the executor sandbox is not a mitigation
here. Whatever the tool reads is then posted into the message history
both agents keep reasoning over.

Uses the per-param call_uses_unnormalized_path_param, matching CSDK-004.
LangChain had no idempotency rule; CrewAI (CREW-006), Pydantic AI
(PYD-007), MCP (MCP-007), Claude SDK (CSDK-006), and OpenAI (OAI-009) all
ship one.

Repetition is routine in the LangChain agent loop rather than
exceptional: an AgentExecutor step whose observation reads as
inconclusive — a timeout, a truncated response, an error string returned
under handle_tool_error — leads the model to re-reason about the same goal
and re-issue the same action, with no way to know the first call already
committed. with_retry and external retry wrappers compound it, since a
network failure after the side effect landed looks identical to one
before.
Ports the CSDK-017/018 description-quality pair to Pydantic AI. PYD-001
only checks that a docstring exists, so a tool whose docstring reads
"TODO" or "Gets data." passes today while giving the model no selection
signal.

The cost is higher in this pack than in the Claude SDK, and the rule text
says so: Pydantic AI also parses the docstring's parameter sections into
the argument schema, so a placeholder or one-clause docstring strips the
per-argument guidance at the same time as the tool-level guidance, and
nothing in the pipeline reports the omission.
Ports the CSDK-017/018 description-quality pair to LangChain. LC-001 only
checks that a docstring exists, so a tool whose docstring reads "TODO" or
"Gets data." passes today while giving the model no selection signal.

The framing is LangChain-specific on both counts. Mis-selection scales
badly here because agents are routinely handed a dozen or more tools at
once, and each mis-selected call burns an iteration against LC-102's
max_iterations budget, so a run can exhaust its steps on tools it picked
badly and return nothing useful. LC-018's fix also points at the @tool
decorator's explicit description= for the case where the function name is
genuinely ambiguous.
Ports the CSDK-017/018 description-quality pair to CrewAI. CREW-001 only
checks that a docstring exists, so a tool whose docstring reads "TODO" or
"Gets data." passes today while giving the agent no selection signal.

The consequence travels further in a crew than in a single-agent SDK: an
agent that picks the wrong tool produces a task output threaded forward as
context to every task behind it, so one undescribed tool degrades work the
owning agent never touches, and CREW-104 delegation lets peers reach the
tool on the strength of the same placeholder. Each mis-selected call also
spends one of the agent's CREW-110 max_iter steps.
Ports the CSDK-017/018 description-quality pair to AutoGen. AG2-007 only
checks that a docstring exists, so a tool whose docstring reads "TODO" or
"Gets data." passes today while giving the assistant no selection signal.

How AutoGen fails on a bad guess is what makes this expensive: a wrongly
chosen tool returns something that does not answer the assistant's intent,
the assistant reads that as an inconclusive step and proposes another
call, and the pair trades rounds until AG2-004's max_round or AG2-006's
auto-reply cap ends the chat. The budget goes on a selection problem no
error message points at. Both fixes name register_for_llm's description=
as the clearer place to say it.
The Vercel AI pack had no path-safety rule; Claude SDK (CSDK-004/012),
OpenAI (OAI-006), ADK (ADK-004), and MCP (MCP-005) all ship one. Mirrors
CSDK-012, the TypeScript half of that pair, including its coarse-signal
caveat: it flags any filesystem write rather than only unnormalized paths,
because TS path-normalization analysis is not yet wired.

The deployment shape is what makes it worth flagging here. Vercel AI
tools typically run inside the same server process as the request
handler rather than in a sandbox, so a model-steered write inherits the
application's own filesystem permissions.
Ports OAI-010 to Pydantic AI. The loss is larger in this SDK than in the
OpenAI pack, and the rule text says so: Pydantic AI emits OpenTelemetry
spans for each run and tool call, so everything around a print is already
correlated to a trace and the print is the one diagnostic landing outside
it, unattached to the run that produced it. The fix names attaching the
detail to the current span (Logfire or the OTel API) alongside the module
logger.
Ports OAI-010 to LangChain. The loss is larger here than in the OpenAI
pack, and the rule text says so: LangChain already threads every tool
start, end, and error through its callback system into LangSmith or
whichever tracer is configured, so a bare print is the one diagnostic
landing outside that — detached from the run and the step that produced
it, and simply absent the moment anyone debugs from a trace rather than a
terminal.
Ports OAI-010 to CrewAI. CrewAI makes it worse than a lost log line: a
crew running with verbose=True is already writing its own narration to
stdout, so a tool print is interleaved into run commentary from several
agents at once with nothing marking which agent, task, or tool call
emitted it. What looks like working diagnostics in a terminal is
unattributable the moment the crew runs anywhere else.
Ports OAI-010 to AutoGen. AutoGen makes it especially deceptive: the
conversation itself is narrated to stdout, so a tool print lands mid
transcript and reads as though it were part of the exchange, when it is
invisible to the agents reasoning over it and carries no marker for which
round or tool call produced it. Anyone reconstructing the run from a log
sink rather than a terminal loses it entirely.
…nsport

MCP had no observability rule. OpenAI ships OAI-010 and ADK ships ADK-009
for the same pattern, both at low severity as a lost-diagnostic problem.

It is not a lost-diagnostic problem here, which is why this ships at
medium rather than low. On a stdio server — the FastMCP default, and the
usual way an editor or desktop client launches a server — stdout is the
protocol channel, so a print interleaves with the newline-delimited
JSON-RPC frames the client is parsing. The client hits a parse error on a
line that is not JSON and drops the response or tears down the
connection, and the symptom does not resemble the cause: a tool call that
returns nothing, or a mid-session disconnect, traced back to a print that
reads like harmless debugging.

Confidence 0.7 rather than higher because a server run over HTTP or SSE
escapes the corruption, though it still loses the diagnostic. The fix
names stderr specifically, which the transport leaves alone.
Claude SDK and MCP were the two mature packs with no observability rule;
OpenAI ships OAI-010 and ADK ships ADK-009 for the same pattern.

Two Claude-SDK-specific consequences go beyond the lost log line OAI-010
describes. A tool defined in a Python SDK server is commonly served to the
agent over an MCP stdio transport, where stdout carries the JSON-RPC
frames and a loose print makes the client hit a parse error on a line that
is not JSON. And when the SDK is driven programmatically, the host
application is already reading the SDK's own message stream, so tool
prints land interleaved with it rather than in the application's logs,
attributable to no particular turn or tool call.
CSDK-003 covers the Python side; the TypeScript half was missing even
though this pack ships TS rules throughout (CSDK-010..014, CSDK-120..131).
OpenAI (OAI-016, OAI-024) and the Vercel AI SDK (VAI-011) already use
has_http_call_without_timeout for exactly this.

Node's fetch has no implicit deadline, so an unresponsive host stalls the
conversation rather than failing it: the model gets no result and no
error, and a max_turns cap does not help because the run is stuck inside
one turn rather than taking too many. Compounds with CSDK-013 — a
model-controlled URL that also cannot time out can be pointed at an
internal host that never answers.
MCP-004 covers the Python side; the TypeScript half was missing even
though the pack ships TS rules (MCP-011, MCP-013). OpenAI (OAI-016,
OAI-024) and the Vercel AI SDK (VAI-011) already use
has_http_call_without_timeout for exactly this.

The stall crosses the server's trust boundary rather than staying local:
the connecting client waits on a JSON-RPC response that never arrives,
and MCP gives it no way to cancel an in-flight tool call, so it is left to
its own timeout, an abandoned request, or a hung session, with nothing in
the response saying why. On a stdio server a handler parked on a dead
socket is holding the single process that serves the connection.
…no timeout

ADK-003 covers the Python side; the TypeScript half was missing even
though the pack ships TS rules (ADK-013, ADK-015, ADK-016, ADK-109).
OpenAI (OAI-016, OAI-024) and the Vercel AI SDK (VAI-011) already use
has_http_call_without_timeout for exactly this.

The composition ADK encourages makes the stall worse rather than better:
inside a SequentialAgent a stalled tool blocks every step after it, and
inside a ParallelAgent the fan-out waits on its slowest branch, so one
unresponsive host stalls work unrelated to it. ADK-108's max_iterations
bounds how many times a LoopAgent goes round, not how long one tool call
may run, so no configured limit breaks the stall.

Numbered ADK-114 to leave room for ADK-111 in the open PR #51 and
ADK-112 in my PR #79.
Ports the CSDK-017/018 description-quality pair to the OpenAI Agents SDK.
OAI-001 only checks that a docstring exists, so a tool whose docstring
reads "TODO" or "Gets data." passes today while giving the model no
selection signal.

Two OpenAI-specific points. Strict schemas do not compensate: OAI-003's
strict_mode constrains the shape of the arguments, never whether calling
this tool was the right move, so a placeholder description produces a
well-formed call to the wrong tool. And with handoffs the cost is not
just a wrong call — an agent picks between its own tools and its peers'
on the strength of these strings, so a thin description can route the
whole conversation to the wrong agent. Mis-selection also spends a turn
against the OAI-112 max_turns budget with nothing in a trace attributing
it to the description.
Ports the CSDK-017/018 description-quality pair to MCP. MCP-001 only
checks that a docstring exists, so a tool whose docstring reads "TODO" or
"Gets data." passes today while giving connecting clients no selection
signal.

A placeholder costs more across a protocol boundary than inside an
in-process SDK. The consumer has no other context to fall back on — it
cannot read the source, the neighboring tools, or the project's docs the
way a developer in the repo can. The server's authors also never see the
consequence, since mis-selection surfaces in someone else's client
session as a wrong answer rather than as an error on this side. And a
client typically connects several servers at once, so the model chooses
across all of them from these strings alone and a thin description
competes badly against a well-described tool from an unrelated server.
Ports the CSDK-017/018 description-quality pair to the Google ADK.
ADK-001 only checks that a docstring exists, so a tool whose docstring
reads "TODO" or "Gets data." passes today while giving the model no
selection signal.

Two ADK-specific points. The ADK parses the docstring for the
per-parameter descriptions that accompany the generated schema, so a
placeholder or one-clause docstring strips argument-level guidance at the
same time as tool-level guidance. And in an agent tree the mis-selection
does not stay local: a tool picked wrongly inside one branch produces
output the following agents treat as established fact, so the error is
laundered into shared session state rather than surfacing where it began.
ADK-102/107's before_tool_callback can block a call it recognizes as
wrong but cannot supply the judgment the description was meant to give.

Numbered ADK-115/116 to leave room for ADK-111 in the open PR #51 and the
IDs used by my other open PRs.
…ty rules

CSDK-017/018 cover the Python side; the TypeScript half was missing.
CSDK-014 only checks that a description exists, so a tool whose
description reads "TODO: describe this tool." or "Gets data." passes today
while leaving the tool in exactly the state CSDK-014 exists to prevent.

The gap costs more on the TypeScript side than in Python, and the rule
text says so: CSDK-014's own explanation notes there is no docstring
fallback, so the description argument is the entire prompt-side account of
the tool. The Zod input schema does not compensate — it constrains the
shape of the arguments once the model has decided to call this tool, and
says nothing about whether calling it was the right move.
The TypeScript counterpart to MCP-025/026. MCP-011 only checks that a
description exists, so a registration described "TODO: describe this
tool." or "Gets data." passes today while publishing no selection signal
to connecting clients.

Same protocol-boundary argument as the Python pair — the consumer has no
fallback context, the server's authors never observe the mis-selection,
and the model chooses across every connected server from these strings
alone — plus the TypeScript-specific point that the Zod input schema
constrains the arguments once the model has chosen this tool, not whether
choosing it was right.
… rules

The TypeScript counterpart to OAI-025/026. OAI-022 only checks that a
description exists, so a tool described "TODO: describe this tool." or
"Gets data." passes today while leaving the tool in exactly the state
OAI-022 exists to prevent.

The Zod parameters schema does not compensate: it constrains the shape of
the arguments once the model has decided to call this tool, never whether
that decision was right, so a placeholder yields a well-formed call to the
wrong tool. With handoffs the reach is wider — an agent picks between its
own tools and its peers' from these strings, so the stub can route the
conversation to the wrong agent rather than merely the wrong function.
The TypeScript counterpart to LC-018/019. LC-010 only checks that a
description exists, so a tool described "TODO: describe this tool." or
"Gets data." passes today while giving the model no selection signal.

LangChain.js has no docstring to fall back on, so the description field is
the entire account of the tool the model sees, and the Zod schema does not
compensate — it constrains the arguments once the model has chosen this
tool, never whether choosing it was right. Mis-selection scales badly here
because an agent is routinely handed a dozen or more tools at once, and
each wrong pick spends an iteration against the maxIterations bound
LC-111 checks for.
MCP-005 covers the Python path-safety case; the TypeScript half was
missing. Mirrors CSDK-012, including its coarse-signal caveat — it flags
any filesystem write rather than only unnormalized paths, because TS
path-normalization analysis is not yet wired.

Deployment is what sharpens this for MCP. A stdio server is launched as a
subprocess by whatever client the user is running, so it inherits that
user's own filesystem permissions rather than a service account's, and a
write escaping its intended directory reaches the user's home directory,
dotfiles, and SSH keys. The server also cannot see the injection: it
receives a well-formed tools/call for a path it has no way to distinguish
from a legitimate one.
OAI-006 covers the Python path-safety case; the TypeScript half was
missing. Mirrors CSDK-012, including its coarse-signal caveat — it flags
any filesystem write rather than only unnormalized paths, because TS
path-normalization analysis is not yet wired.

The guardrail story does not cover this, which is the point worth making
in an SDK built around guardrails: OAI-101 concerns input guardrails on
the agent, which screen what enters the conversation, not what a tool does
with an argument once the model has produced it, and a call reaching
execute() has already passed whatever guardrails were configured. Tools
here also typically run in the same server process as the request handler
rather than a sandbox.
…filesystem

ADK-004 covers the Python path-safety case; the TypeScript half was
missing. Mirrors CSDK-012, including its coarse-signal caveat — it flags
any filesystem write rather than only unnormalized paths, because TS
path-normalization analysis is not yet wired.

The callback gate does not close this by default: ADK-102 and ADK-107 are
about before_tool_callback being present at all, and a callback that is
present still has to inspect the path itself and know which root is
allowed. Being wired up is not the same as containing anything. Agent
composition widens the reach too — any branch listing this tool can reach
it, and a write performed in one branch is visible to every agent reading
the shared session state afterward, so the fix belongs inside the tool.
Ports the ambiguous-name check to langchain. Claude SDK (CSDK-007), OpenAI
(OAI-007), ADK (ADK-007), and MCP (MCP-003) all ship it; the five newer
packs had none.

The tool name sits directly beside the description in what the model sees,
so it is half the selection signal, and a generic verb like process or
handle spends that half on nothing.
Ports the ambiguous-name check to crewai. Claude SDK (CSDK-007), OpenAI
(OAI-007), ADK (ADK-007), and MCP (MCP-003) all ship it; the five newer
packs had none.

The tool name sits directly beside the description in what the model sees,
so it is half the selection signal, and a generic verb like process or
handle spends that half on nothing.
Ports the ambiguous-name check to autogen. Claude SDK (CSDK-007), OpenAI
(OAI-007), ADK (ADK-007), and MCP (MCP-003) all ship it; the five newer
packs had none.

The tool name sits directly beside the description in what the model sees,
so it is half the selection signal, and a generic verb like process or
handle spends that half on nothing.
Ports the ambiguous-name check to pydantic_ai. Claude SDK (CSDK-007), OpenAI
(OAI-007), ADK (ADK-007), and MCP (MCP-003) all ship it; the five newer
packs had none.

The tool name sits directly beside the description in what the model sees,
so it is half the selection signal, and a generic verb like process or
handle spends that half on nothing.
@sairenchristianbuerano
sairenchristianbuerano merged commit 95fe7cf into main Sep 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants