Repository navigation
Add acumatica-silent-success-traps skill to the DEV plugin - #6
saratcvemuri wants to merge 1 commit into
Conversation
Accepted proposal Acumatica#4. Adds a skill for the class of Acumatica API behaviors that return success and do nothing, or return empty instead of an error -- there is no exception to catch and no error code to branch on, so an integration reports success to its caller and an agent reports a confident wrong answer. The skill's rule is that a 2xx is not evidence of effect and an empty result is not evidence of absence, and it covers five instances: a write discarded because the principal lacks edit rights, a write discarded because the field is not settable through the endpoint, a body-less 200 (distinct from {"value":[]}), a $filter referencing a calculated column, a parameterized Generic Inquiry queried by its base name, and an inquiry whose name contains a URL path separator and is therefore unreachable though it reports as exposed. Scope is deliberately separated from acumatica-integration-diagnostics: this fires when writing or reviewing code that issues these calls, not when repairing legacy integration clients. The description states that boundary so both skills do not load for one request. Behaviors were observed on Acumatica 2025 R2 SaaS; the skill scopes them explicitly and notes that none is documented as a contract. Per the proposal thread, minimal verification against 2026 R1 will be done on the maintainer side. Includes the GitHub Copilot discovery adapter, the Codex agents/openai.yaml interface, the DEV/README.md registration, and Verification/acumatica-silent-success-traps-review.md (no open P1 or P2 findings; three P3 items recorded, one left open deliberately because all three sibling skills share the same description voice). DEV plugin manifests bumped 1.0.2 -> 1.1.0 on all three platforms for the added skill. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Sarat Vemuri <saratvemuri@hallboys.com>
| For an unattended consumer, and for any agent that cannot inspect the design, prefer a | ||
| parameter-free copy of the inquiry, or read the underlying contract entity. | ||
|
|
||
| ## Before you trust that an inquiry is exposed |
There was a problem hiding this comment.
@saratcvemuri , as per comment from @dnaumov here: #4 (comment), this is no longer the case for 2026 R1
| integration reports success to its caller and an agent reports a confident wrong answer. | ||
| Verify by reading back, not by reading the status code. | ||
|
|
||
| Each behavior below was observed on **Acumatica 2025 R2 SaaS**. These are platform |
There was a problem hiding this comment.
The statement that all behaviors were observed on 2025 R2 and that “none of them is documented” is inaccurate. Formula filtering restrictions and _WithParameters are documented.
Could you replace lines 21–24 (line 21) with per-trap applicability?
- REST write/read-back trap: confirmed for 2025 R2 and 2026 R1.
- Calculated-column restriction: documented.
- Body-less 200 response: observed behavior for 2025 R2 and 2026 R1.
- Parameterized GI behavior: _WithParameters is documented; the silent empty base-query result is observed.
- Path-separator issue: 2025 R2 only; fixed in 2026 R1.
| ## Before you report a write as done | ||
|
|
||
| Resolve it to a verified 2xx **and** a read-back that confirms the specific fields you | ||
| sent. Two causes discard a write with an identical HTTP 200: |
There was a problem hiding this comment.
“Two causes discard a write” at line 29 (line 29) is too restrictive. The documentation already identifies graph logic as another reason a requested value may not persist. (See Documentation/IntegrationDevelopmentGuide/IntegrationDev_RESTExample_Basic_Update_Record.md (line 63) and the equivalent PATCH contract in Documentation/IntegrationDevelopmentGuide/IntegrationDev_RESTExample_Basic_Update_Particular_Fields.md .)
Consider to use “Known causes include” and add:
- Insufficient edit rights despite readable form access.
- Field not settable through the endpoint.
- Graph logic overriding or rejecting the requested value.
- PUT comparison behavior; PATCH may be appropriate when only specified fields should be updated.
The invariant remains the same: independently read back the intended fields.
|
|
||
| Distinguish three cases that all look like "nothing found": | ||
|
|
||
| - **A body-less 200.** Standard OData returns `{"value":[]}` for a genuinely empty result, |
There was a problem hiding this comment.
Avoid claiming that the query “never ran”
A body-less 200 proves that the caller received no usable result; it does not necessarily prove that no database query executed.
Change lines 49–56 and 92 from “query never ran” to something like:
The response does not establish an empty result set. Treat the query outcome as indeterminate until it is retried or reformulated.
That preserves the safety behavior without asserting an unobservable implementation detail.
| @@ -0,0 +1,93 @@ | |||
| --- | |||
| name: acumatica-silent-success-traps | |||
There was a problem hiding this comment.
The skill currently says “Acumatica API” broadly, but it mixes:
- Contract-based REST writes.
- Generic Inquiry–based OData reads.
Name the applicable API surface in each section. This prevents agents from applying {"value":[]} semantics to contract-based REST responses or GI OData rules to endpoint entities.
Implements the skill accepted in #4.
What this adds
A skill for the class of Acumatica API behaviors that return success and do nothing, or return empty instead of an error. There is no exception to catch and no error code to branch on, so an integration reports success to its caller and an agent reports a confident wrong answer.
The rule it teaches: a 2xx is not evidence of effect, and an empty result is not evidence of absence. Five instances, each independently reproduced:
{"value":[]}and means the query never ran.$filterreferencing a calculated column — documented as unsupported, but reported as an empty-body 200 rather than a 400._WithParametersFunctionImport.Plus one that surfaced while drafting: an inquiry whose name contains a URL path separator is accepted by Expose via OData and appears in
$metadata, but 404s and never reaches the service document — it reports as published and simply never works.Scope boundary
Deliberately separated from
acumatica-integration-diagnostics: this fires when writing or reviewing code that issues these calls, not when repairing legacy integration clients. The description states that boundary explicitly so both skills do not load for one request. Happy to merge it into the existing skill instead if you would prefer that — the proposal offered either.Version scoping
Behaviors were observed on Acumatica 2025 R2 SaaS. The skill scopes them once, beside the rule, and states that none of them is documented as a contract — which is why the scope is there rather than being boilerplate. Per the proposal thread, minimal verification against 2026 R1 is expected on the maintainer side; happy to adjust the wording to whatever you confirm.
Included
DEV/skills/acumatica-silent-success-traps/SKILL.md— canonical skill, 87 lines / 795 words, noreferences/orscripts/neededDEV/skills/acumatica-silent-success-traps/agents/openai.yaml— Codex interface.github/skills/acumatica-silent-success-traps/SKILL.md— GitHub Copilot discovery adapterDEV/README.md— registrationVerification/acumatica-silent-success-traps-review.md— no open P1 or P2 findings; three P2 items were found and fixed before submission, three P3 items are recorded, one left open deliberately (the imperative-voice description matches all three sibling skills, so changing it would make this one the outlier)1.0.2→1.1.0on all three platforms for the added skillVerification run locally
.github/scripts/validate-contribution.ps1 -BaseSha upstream/2026R1→ passed (6 skills, 6 Copilot adapters, 2 plugins). The skill was also reviewed with this repository's ownCommon/agent-tooling/skills/verifying-skills, which is where the six findings above come from.No customer data: a scan for instance hostnames, branch and warehouse codes, inquiry names, and document numbers returns clean. All content is original, contributed under GPL-3.0-only with DCO sign-off.
🤖 Generated with Claude Code