Skip to content

Add acumatica-silent-success-traps skill to the DEV plugin - #6

Open
saratcvemuri wants to merge 1 commit into
Acumatica:2026R1from
saratcvemuri:skill/add-silent-success-traps
Open

saratcvemuri wants to merge 1 commit into
Acumatica:2026R1from
saratcvemuri:skill/add-silent-success-traps

Conversation

@saratcvemuri

Copy link
Copy Markdown

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:

  1. A write discarded because the principal lacks edit rights on the target form — HTTP 200, nothing persisted.
  2. A write discarded because the field is not settable through the endpoint — identical 200, different cause.
  3. A body-less 200, which is not the same as {"value":[]} and means the query never ran.
  4. A $filter referencing a calculated column — documented as unsupported, but reported as an empty-body 200 rather than a 400.
  5. A parameterized Generic Inquiry queried by its base name — 0 rows, 200, no error, because parameters bind only through the separate _WithParameters FunctionImport.

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, no references/ or scripts/ needed
  • DEV/skills/acumatica-silent-success-traps/agents/openai.yaml — Codex interface
  • .github/skills/acumatica-silent-success-traps/SKILL.md — GitHub Copilot discovery adapter
  • DEV/README.md — registration
  • Verification/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)
  • DEV plugin manifests bumped 1.0.2 → 1.1.0 on all three platforms for the added skill

Verification 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 own Common/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

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>
@saratcvemuri
saratcvemuri requested a review from a team as a code owner September 9, 2026 11:34
@github-actions github-actions Bot added the area:dev Changes to DEV resources label Sep 9, 2026
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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“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,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:dev Changes to DEV resources

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants