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

80 lines
4.6 KiB
Markdown

# 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:
```json
{"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`:
```json
{"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:
```json
{"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":"..."}]}}
```
3. Keep the order: save, then (only when the save fails) check. A good write costs no extra call.
4. 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.
5. 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).