Files
abap-llm/docs/epod-syntax-hint.md

4.6 KiB

EPOD: syntax messages for a failed write

Status: proposal (2026-10-05). Kral decides later if it goes into the EPOD server. The harness proxy does this today (harness/proxy.py, harness/localcheck.py). The proxy is not changed by this note.

Problem

sap_push_source fails with only this result when SAP does not accept the source:

{"success":false,"error":"[WRITE] An error occured during the save operation. The changes were not stored."}

Example cause: METHODS run IMPORTING iv TYPE c LENGTH 4. (a generic type with LENGTH in a method signature). The model cannot see the cause, so it cannot repair. In the pilot this was 7 failures in 3 tasks.

sap_syntax_check does not help: it reads the stored version (inactive or active), not the rejected source. Tested 2026-10-05: after the failed write it returned {"version":"inactive","errorCount":0,"findings":[]} (it checked the empty stub that sap_create_object made).

What the proxy does now

  1. A result of sap_push_source is a bare save failure when the JSON has success:false, the error text contains "during the save operation", and activation.messages is empty (localcheck.is_bare_save_failure).
  2. The proxy takes the source from the call arguments and runs the abaplint parser on it (localcheck.parser_messages): one temporary file named by object type (<name>.clas.abap, <name>.clas.testclasses.abap for includeType testclasses, .intf.abap, .prog.abap, <group>.fugr.<name>.abap, .ddls.asddls), rule parser_error only, syntax version from the task release target (default v758). TABL, FUGR and other types without a parser: no hint.
  3. It adds a syntaxCheck field to the same JSON object (localcheck.augment). The original result stays in the trajectory log as raw_result:
{"success":false,
 "error":"[WRITE] An error occured during the save operation. The changes were not stored.",
 "syntaxCheck":{"source":"local abaplint (the rejected source, not stored in SAP)",
                "messages":[{"line":3,"severity":"E",
                             "message":"Statement does not exist in the configured ABAP version(or a parser error), \"METHODS\""}]}}

Limits: abaplint reports the line and the statement that it cannot parse, not the SAP reason ("TYPE c LENGTH in a signature is not allowed"). It does not know SAP rules that are not syntax (reserved words in DDL, name length, CDS annotations). It finds only parser errors, no type or semantic errors.

Same logic in the EPOD server

The server has the source and the lock, so it can do better than abaplint: check the rejected source with the SAP syntax check, without saving it.

  1. In sap_push_source (and sap_push_element), when the save step fails, run the ADT syntax check on the source in the request. Eclipse checks unsaved editor content through the ADT check run (not tested by the harness yet): POST /sap/bc/adt/checkruns?reporters=abapCheckRun with a chkrun:checkObject that has the object URI and an artifacts entry with the new source (base64) as chkrun:content . The answer has chkrun:checkMessage entries with chkrun:uri (line and column), chkrun:type (E, W), and chkrun:shortText.
  2. Add the messages to the failed result in the same field shape as the proxy, so a client does not care who made them:
{"success":false,
 "error":"[WRITE] An error occured during the save operation. The changes were not stored.",
 "syntaxCheck":{"source":"sap_checkrun (the rejected source, not stored)",
                "messages":[{"line":3,"column":41,"severity":"E","message":"..."}]}}
  1. Keep the order: save, then (only when the save fails) check. A good write costs no extra call.
  2. Extend sap_syntax_check with an optional source argument (check this source, do not store it). The model can then check before it writes. This is useful by itself, also when no write failed.
  3. If the check run itself fails (lock, timeout), return the original result without syntaxCheck. The field is optional.

Open points for EPOD: whether the checkrun accepts an unsaved source for every object type (classes with include types, function modules in a group, CDS); the harness would test this on A4H first (null/oracle runs only).

What stays in the harness either way

  • A server-side change replaces localcheck.py for the types that the server supports. The proxy code checks syntaxCheck first: if the field is there, it adds nothing.
  • Training data from before the change has the abaplint messages, data after it has the SAP messages. Keep the syntaxCheck.source text so the two kinds can be told apart (the field is in every record).