abapgit-agent 1.13.2 → 1.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/abap/CLAUDE.md CHANGED
@@ -70,24 +70,93 @@ abapgit-agent ref --list-topics
70
70
  ```
71
71
 
72
72
  The folder is configured in `.abapGitAgent` (property: `folder`):
73
- - If `folder` is `/src/` → files go in `src/` (e.g., `src/zcl_my_class.clas.abap`)
74
- - If `folder` is `/abap/` → files go in `abap/` (e.g., `abap/zcl_my_class.clas.abap`)
73
+ - If `folder` is `/src/` → files go in `src/`
74
+ - If `folder` is `/abap/` → files go in `abap/`
75
75
 
76
76
  **Also check naming conventions before creating any new object:**
77
77
 
78
78
  ```
79
- 1. Check guidelines/objects.local.md ← project-specific overrides (if file exists)
80
- 2. Fall back to guidelines/objects.md default Z/Y prefix conventions
79
+ 1. Check guidelines/objects.local.md ← this project's actual conventions (if file exists)
80
+ 2. No objects.local.md? customer namespace project, use Z/Y defaults
81
81
  ```
82
82
 
83
- `objects.local.md` is created by `abapgit-agent init` and is never overwritten by updates it holds project-specific prefixes (e.g. `YCL_` instead of `ZCL_`).
83
+ `objects.local.md` is never overwritten by updates. It specifies the production naming
84
+ pattern — which may be customer namespace (`ZCL_*`, `YCL_*`), SAP namespace (`CL_*`),
85
+ or SAP registered namespace (`/NAMESPACE/CL_*` — also SAP namespace, not customer).
86
+ Never assume Z/Y prefix without checking.
87
+
88
+ **SAP namespace vs customer namespace:**
89
+ - **Customer namespace**: `Z*`, `Y*` objects; `Z*`, `Y*`, `$*` packages — owned by the customer
90
+ - **SAP namespace**: everything else (`CL_*`, `IF_*`, `/NAMESPACE/*`) — delivered by SAP
84
91
 
85
92
  ---
86
93
 
87
- ### 3. Create XML Metadata / Local Classes
94
+ ### 3. Creating a New ABAP Object — Files to Write and Package Assignment
88
95
 
89
- Each ABAP object needs an XML metadata file. Local helper/test-double classes use separate `.locals_def.abap` / `.locals_imp.abap` files.
90
- See `guidelines/object-creation.md` run: `abapgit-agent ref --topic object-creation`
96
+ ```
97
+ WRONG: Only write the .abap source file
98
+ ✅ CORRECT: Every object needs both a source file AND an XML metadata file
99
+ XML-only objects (TABL, STRU, DTEL, TTYP) need ONLY the XML file
100
+ ```
101
+
102
+ Use the object name from `objects.local.md` (or `objects.md` as fallback) in place of `<name>`:
103
+
104
+ | Object Type | Source File | XML File |
105
+ |-------------|-------------|----------|
106
+ | Class (CLAS) | `<name>.clas.abap` | `<name>.clas.xml` |
107
+ | Interface (INTF) | `<name>.intf.abap` | `<name>.intf.xml` |
108
+ | Program (PROG) | `<name>.prog.abap` | `<name>.prog.xml` |
109
+ | CDS View (DDLS) | `<name>.ddls.asddls` | `<name>.ddls.xml` |
110
+ | Table (TABL) | *(none)* | `<name>.tabl.xml` |
111
+ | Structure (STRU) | *(none)* | `<name>.stru.xml` |
112
+ | Data Element (DTEL) | *(none)* | `<name>.dtel.xml` |
113
+ | Table Type (TTYP) | *(none)* | `<name>.ttyp.xml` |
114
+
115
+ **Package assignment — determine the package, then follow the confirmation rule below:**
116
+
117
+ ```
118
+ 1. Check objects.local.md for package rules ← use them directly
119
+ 2. No package rules in objects.local.md?
120
+ └── Read .abapGitAgent → get the package property → use as root
121
+ Run: abapgit-agent tree --package <root>
122
+ ├── Only one package found → use it directly
123
+ └── Multiple packages found → present options, ask user to choose
124
+ 3. No package in .abapGitAgent?
125
+ └── Ask the user for the root package
126
+ ```
127
+
128
+ **Confirmation before writing files — depends on namespace:**
129
+
130
+ ```
131
+ Object name starts with Z* or Y* AND package starts with Z*, Y*, or $*?
132
+ └── Customer namespace object in customer package
133
+ → Write files directly. No confirmation needed.
134
+
135
+ Anything else (SAP namespace object, or SAP-delivered package)?
136
+ └── Show a creation summary and wait for explicit confirmation:
137
+
138
+ "I'm going to create the following:
139
+
140
+ Object: <NAME> (<Type>)
141
+ Package: <PACKAGE>
142
+ Files: <folder>/<name>.<ext>.abap
143
+ <folder>/<name>.<ext>.xml
144
+
145
+ Shall I proceed?"
146
+ ```
147
+
148
+ ```
149
+ ❌ WRONG: Write files without showing the summary for SAP namespace objects
150
+ ❌ WRONG: Run abapgit-agent tree and pick a package yourself
151
+ ✅ CORRECT: Customer namespace → write directly
152
+ ✅ CORRECT: SAP namespace → always show summary, wait for confirmation
153
+ ```
154
+
155
+ > **Tip for project setup**: Add package rules to `objects.local.md` so Claude never
156
+ > needs to ask. See `guidelines/objects.md` for examples.
157
+
158
+ → For exact XML templates: `abapgit-agent ref --topic abapgit`
159
+ → For local helper/test-double class files: `abapgit-agent ref --topic object-creation`
91
160
 
92
161
  ---
93
162
 
@@ -102,14 +171,15 @@ Each ABAP object needs an XML metadata file. Local helper/test-double classes us
102
171
 
103
172
  ```bash
104
173
  # Check syntax of local code (no commit/push needed)
105
- abapgit-agent syntax --files src/zcl_my_class.clas.abap
106
- abapgit-agent syntax --files src/zc_my_view.ddls.asddls
174
+ # Use the actual filename from your project (name comes from objects.local.md)
175
+ abapgit-agent syntax --files src/<name>.clas.abap
176
+ abapgit-agent syntax --files src/<name>.ddls.asddls
107
177
 
108
178
  # Check multiple INDEPENDENT files
109
- abapgit-agent syntax --files src/zcl_utils.clas.abap,src/zcl_logger.clas.abap
179
+ abapgit-agent syntax --files src/<name1>.clas.abap,src/<name2>.clas.abap
110
180
  ```
111
181
 
112
- **For other types (DDLS, FUGR, TABL, etc.)**: Skip syntax, proceed to commit/push/pull.
182
+ **For other types (FUGR, TABL, etc.)**: Skip syntax, proceed to commit/push/pull.
113
183
 
114
184
  **Why use syntax command?**
115
185
  - Catches syntax errors BEFORE polluting git history with fix commits
@@ -126,11 +196,11 @@ When checking multiple files, each is validated in isolation:
126
196
  **For dependent files, skip `syntax` and use `pull` instead:**
127
197
  ```bash
128
198
  # ❌ BAD - Interface and implementing class (may show false errors)
129
- abapgit-agent syntax --files src/zif_my_intf.intf.abap,src/zcl_my_class.clas.abap
199
+ abapgit-agent syntax --files src/<intf_name>.intf.abap,src/<class_name>.clas.abap
130
200
 
131
201
  # ✅ GOOD - Use pull instead for dependent files
132
202
  git add . && git commit && git push
133
- abapgit-agent pull --files src/zif_my_intf.intf.abap,src/zcl_my_class.clas.abap
203
+ abapgit-agent pull --files src/<intf_name>.intf.abap,src/<class_name>.clas.abap
134
204
  ```
135
205
 
136
206
  **Note**: `inspect` still runs against ABAP system (requires pull first). Use `syntax` for pre-commit checking.
@@ -256,11 +326,29 @@ After activating a class, stop and tell the user: `"Class is activated. Run with
256
326
 
257
327
  ---
258
328
 
259
- ### 10. Probe ClassesUse `scratchWorkspace` When Required
329
+ ### 10. Probe and PoC Objects Always Z/Y, Never in SAP Packages
330
+
331
+ ```
332
+ ❌ WRONG: Create a probe/PoC object with the project's SAP namespace prefix
333
+ ❌ WRONG: Assign a probe/PoC object to an SAP-delivered package
334
+ ✅ CORRECT: Probe/PoC objects always use Z* or Y* prefix
335
+ ✅ CORRECT: Always assign to a customer namespace package (Z*, Y*, or $*)
336
+ ```
337
+
338
+ This rule applies even on projects where production objects use SAP namespace (`CL_*`, `/NAMESPACE/*`).
339
+
340
+ **Trigger — when `objects.local.md` shows a SAP namespace prefix (`CL_*`, `IF_*`, `/NAMESPACE/*`)
341
+ and the user asks to create a new object, always ask first:**
342
+
343
+ ```
344
+ "Is this a production object, a PoC (will persist, needs its own package/repo),
345
+ or a probe (throwaway, run once)?"
346
+ ```
260
347
 
261
- By default, probe/throwaway classes may be created in the current project. When `disableProbeClasses: true` is set in `.abapgit-agent.json`, they must go to `scratchWorkspace` instead. If `scratchWorkspace` is also not configured, refuse and guide the user to set it up.
348
+ Never assume wait for the user's answer before proceeding.
262
349
 
263
- See `guidelines/run-probe-classes.md` run: `abapgit-agent ref --topic run-probe-classes`
350
+ For full decision flow (how to determine namespace, probe vs PoC, scratchWorkspace,
351
+ pocWorkspace, setup instructions): `abapgit-agent ref --topic probe-poc`
264
352
 
265
353
  ---
266
354
 
@@ -355,11 +443,11 @@ If workflow mode is `"trunk"` or not set, commit directly to the default branch:
355
443
  ```bash
356
444
  git checkout main # or master/develop (auto-detected)
357
445
  git pull origin main
358
- edit src/zcl_auth_handler.clas.abap
359
- abapgit-agent syntax --files src/zcl_auth_handler.clas.abap
360
- git add . && git commit -m "feat: add authentication handler"
446
+ # edit your ABAP file (name from objects.local.md)
447
+ abapgit-agent syntax --files src/<name>.clas.abap
448
+ git add . && git commit -m "feat: description"
361
449
  git push origin main
362
- abapgit-agent pull --files src/zcl_auth_handler.clas.abap
450
+ abapgit-agent pull --files src/<name>.clas.abap --sync-xml
363
451
  ```
364
452
 
365
453
  ### AI Tool Guidelines
@@ -400,7 +488,14 @@ abapgit-agent pull --files src/zcl_auth_handler.clas.abap
400
488
  2. ✓ Inform the user that run is disabled for this project
401
489
 
402
490
  **When `safeguards.disableProbeClasses = true`:**
403
- 1. ✗ Do not create probe classes in the current project — see Rule 10 and `guidelines/run-command.md`
491
+ 1. ✗ Do not create probe classes in the current project
492
+ 2. ✓ If `scratchWorkspace` is configured → create probe class there (see Rule 10)
493
+ 3. ✗ If `scratchWorkspace` is NOT configured → refuse, tell user to configure it in `.abapGitAgent`
494
+
495
+ **When user requests a PoC object:**
496
+ 1. ✓ Read `pocWorkspace.path` from `.abapGitAgent`
497
+ 2. ✓ If configured → read `{path}/.abapGitAgent` for package, show confirmation summary, wait for user
498
+ 3. ✗ If NOT configured → refuse, tell user to set up a PoC repo and configure `pocWorkspace.path`
404
499
 
405
500
  **When `conflictDetection.mode = "ignore"` or not set:**
406
501
  1. ✓ Run `pull` normally — no conflict flags needed
@@ -425,6 +520,11 @@ abapgit-agent pull --files src/zcl_auth_handler.clas.abap
425
520
  1. ✗ Do not run `abapgit-agent transport release`
426
521
  2. ✓ Inform the user that transport release is disabled for this project
427
522
 
523
+ **After every pull that creates or modifies ABAP objects:**
524
+ 1. ✓ Always pass `--sync-xml` — rewrites any XML metadata files that differ from the ABAP serializer output, amends the commit, and re-pulls so git and the ABAP system stay in sync
525
+ 2. ✓ If pull output shows `⚠️ X XML file(s) differ from serializer output`, re-run immediately with `--sync-xml`
526
+ 3. ✗ Never leave a pull without `--sync-xml` when you authored the objects — abapGit will show **M (modified)** permanently otherwise
527
+
428
528
  ---
429
529
 
430
530
  ### Quick Decision Tree for AI
@@ -435,13 +535,17 @@ abapgit-agent pull --files src/zcl_auth_handler.clas.abap
435
535
  Modified ABAP files?
436
536
  ├─ CLAS/INTF/PROG/DDLS files?
437
537
  │ ├─ Independent files (no cross-dependencies)?
438
- │ │ └─ ✅ Use: syntax → commit → push → pull
538
+ │ │ └─ ✅ Use: syntax → commit → push → pull --sync-xml
439
539
  │ └─ Dependent files (interface + class, class uses class)?
440
- │ └─ ✅ Use: skip syntax → commit → push → pull
441
- └─ Other types (DDLS, FUGR, TABL, etc.)?
442
- └─ Use: skip syntax commit → push → pull → (if errors: inspect)
540
+ │ └─ ✅ Use: skip syntax → commit → push → pull --sync-xml
541
+ └─ Other types (FUGR, TABL, STRU, DTEL, TTYP, etc.)?
542
+ ├─ XML-only objects (TABL, STRU, DTEL, TTYP)?
543
+ │ └─ ✅ Use: skip syntax → commit → push → pull --files abap/ztable.tabl.xml --sync-xml
544
+ └─ FUGR and other complex objects?
545
+ └─ ✅ Use: skip syntax → commit → push → pull --sync-xml → (if errors: inspect)
443
546
  ```
444
547
 
548
+ → For creating new objects (what files to write): `abapgit-agent ref --topic object-creation`
445
549
  → See `guidelines/workflow-detailed.md` — run: `abapgit-agent ref --topic workflow-detailed`
446
550
 
447
551
  ---
@@ -474,6 +578,7 @@ Detailed guidelines are available in the `guidelines/` folder:
474
578
  | `guidelines/debug-session.md` | Debug Session Guide |
475
579
  | `guidelines/debug-dump.md` | Dump Analysis Guide |
476
580
  | `guidelines/run-probe-classes.md` | run Command — AI Guidelines (probe classes, scratchWorkspace) |
581
+ | `guidelines/probe-poc.md` | Probe and PoC — Full Decision Flow |
477
582
  | `guidelines/branch-workflow.md` | Branch Workflow |
478
583
  | `guidelines/workflow-detailed.md` | Development Workflow (Detailed) |
479
584
  | `guidelines/object-creation.md` | Object Creation (XML metadata, local classes) |
@@ -10,6 +10,8 @@ Read the full ABAP development guide by running:
10
10
  abapgit-agent guide
11
11
  ```
12
12
 
13
+ **Never pipe `abapgit-agent guide` or `abapgit-agent ref` through `head`, `tail`, or any other truncation command. Always read the full output.**
14
+
13
15
  This guide covers: development workflow, ABAP syntax guidelines, object naming, unit testing, and debugging.
14
16
 
15
17
  > **Humans:** Full guide online at https://sylvoscai.github.io/abapgit-agent/pages/abap-coding-guidelines.html
@@ -19,23 +19,23 @@ Class | zcl_*.clas.abap | zcl_*.clas.xml
19
19
  Test Class | zcl_*.clas.testclasses.abap | zcl_*.clas.xml
20
20
  Interface | zif_*.intf.abap | zif_*.intf.xml
21
21
  Program | z*.prog.abap | z*.prog.xml
22
- Table | z*.tabl.abap | z*.tabl.xml
23
22
  CDS View (DDLS) | zc_*.ddls.asddls | zc_*.ddls.xml
24
- Data Element | z*.dtel.abap | z*.dtel.xml
25
- Structure | z*.stru.abap | z*.stru.xml
26
- Table Type | z*.ttyp.abap | z*.ttyp.xml
23
+ Table (TABL) | (no .abap file) | z*.tabl.xml
24
+ Structure (STRU) | (no .abap file) | z*.stru.xml
25
+ Data Element (DTEL)| (no .abap file) | z*.dtel.xml
26
+ Table Type (TTYP) | (no .abap file) | z*.ttyp.xml
27
27
  ```
28
28
 
29
- ```
30
- Key XML Settings:
31
- Class EXPOSURE: 2=Public, 3=Protected, 4=Private
32
- Class STATE: 1=Active
33
- Table TABCLASS: TRANSP, POOL, CLUSTER
34
- Table DELIVERY: A=Application, C=Customizing
35
- CDS SOURCE_TYPE: W=View Entity (modern), V=View (legacy)
36
- Test Class XML: <WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
37
- Local Classes: <CLSCCINCL>X</CLSCCINCL>
38
- ```
29
+ > **XML-only objects (TABL, STRU, DTEL, TTYP)** have **no ABAP source file**. abapGit serializes
30
+ > them as XML metadata only. When using `--files`, pass the `.xml` file directly:
31
+ > `abapgit-agent pull --files abap/zmy_table.tabl.xml --sync-xml`
32
+
33
+ > **CRITICAL: Always write XML files with a UTF-8 BOM (`\ufeff`) as the very first character**, before `<?xml ...`.
34
+ > Without the BOM, abapGit shows the object as **"M" (modified)** after every pull because the
35
+ > serializer always produces XML with BOM — and every byte matters.
36
+
37
+ > **CRITICAL: Only include fields that abapGit's serializer actually writes. Never add fields with
38
+ > default values.** Extra fields cause a permanent "M" (modified) diff. Follow the exact templates below.
39
39
 
40
40
  **Searchable keywords**: class xml, interface xml, table xml, cds xml, test class, exposure, serializer, abapgit
41
41
 
@@ -46,14 +46,50 @@ abapGit needs XML files to:
46
46
  - Store object attributes (description, exposure, state, etc.)
47
47
  - Handle object-specific configurations
48
48
 
49
+ ## Field Presence Rules (CRITICAL)
50
+
51
+ abapGit's serializer **omits fields that have their default value**. Writing extra fields causes permanent
52
+ "M" (modified) status in abapGit UI. Follow these rules strictly:
53
+
54
+ ### CLAS — Field Presence Rules
55
+
56
+ | Field | Include when | Omit when |
57
+ |---|---|---|
58
+ | `CLSNAME` | Always | — |
59
+ | `LANGU` | Always | — |
60
+ | `DESCRIPT` | Always | — |
61
+ | `CATEGORY` | Non-standard class (`40`=exception, `05`=test double) | Standard class (default `00`) — **omit** |
62
+ | `EXPOSURE` | **Never for CLAS** | **Always omit** — public (`2`) is the default and is never written |
63
+ | `STATE` | Always (`1`) | — |
64
+ | `CLSCCINCL` | `.clas.locals_def.abap` file exists | No local class files — **omit** |
65
+ | `FIXPT` | Always (`X`) | — |
66
+ | `UNICODE` | Always (`X`) | — |
67
+ | `WITH_UNIT_TESTS` | `.clas.testclasses.abap` file exists | No test class file — **omit** |
68
+ | `MSG_ID` | Class has a message class | No message class — **omit** |
69
+
70
+ **Field order**: `CLSNAME → LANGU → DESCRIPT → [CATEGORY] → STATE → [CLSCCINCL] → FIXPT → UNICODE → [WITH_UNIT_TESTS] → [MSG_ID]`
71
+
72
+ ### INTF — Field Presence Rules
73
+
74
+ | Field | Include when | Omit when |
75
+ |---|---|---|
76
+ | `CLSNAME` | Always | — |
77
+ | `LANGU` | Always | — |
78
+ | `DESCRIPT` | Always | — |
79
+ | `EXPOSURE` | Always (`2`) | — (interfaces always have EXPOSURE, unlike classes) |
80
+ | `STATE` | Always (`1`) | — |
81
+ | `UNICODE` | Always (`X`) | — |
82
+
49
83
  ## Object Types and XML Templates
50
84
 
51
85
  ### Class (CLAS)
52
86
 
53
87
  **Filename**: `src/zcl_my_class.clas.xml`
54
88
 
89
+ **Standard public class** (no local includes, no test class):
90
+
55
91
  ```xml
56
- <?xml version="1.0" encoding="utf-8"?>
92
+ <?xml version="1.0" encoding="utf-8"?>
57
93
  <abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
58
94
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
59
95
  <asx:values>
@@ -61,29 +97,85 @@ abapGit needs XML files to:
61
97
  <CLSNAME>ZCL_MY_CLASS</CLSNAME>
62
98
  <LANGU>E</LANGU>
63
99
  <DESCRIPT>Description of the class</DESCRIPT>
64
- <EXPOSURE>2</EXPOSURE>
65
100
  <STATE>1</STATE>
101
+ <FIXPT>X</FIXPT>
66
102
  <UNICODE>X</UNICODE>
103
+ </VSEOCLASS>
104
+ </asx:values>
105
+ </asx:abap>
106
+ </abapGit>
107
+ ```
108
+
109
+ **Class with test class** (`.clas.testclasses.abap` exists — add `WITH_UNIT_TESTS`):
110
+
111
+ ```xml
112
+ <?xml version="1.0" encoding="utf-8"?>
113
+ <abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
114
+ <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
115
+ <asx:values>
116
+ <VSEOCLASS>
117
+ <CLSNAME>ZCL_MY_CLASS</CLSNAME>
118
+ <LANGU>E</LANGU>
119
+ <DESCRIPT>Description of the class</DESCRIPT>
120
+ <STATE>1</STATE>
67
121
  <FIXPT>X</FIXPT>
122
+ <UNICODE>X</UNICODE>
123
+ <WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
68
124
  </VSEOCLASS>
69
125
  </asx:values>
70
126
  </asx:abap>
71
127
  </abapGit>
72
128
  ```
73
129
 
74
- **Key Fields**:
75
- - `CLSNAME`: Class name (must match filename)
76
- - `DESCRIPT`: Class description
77
- - `EXPOSURE`: Exposure (2 = Public, 3 = Protected, 4 = Private)
78
- - `STATE`: State (1 = Active)
79
- - `UNICODE`: Unicode encoding (X = Yes)
80
- - `FIXPT`: Fixed-point arithmetic (X = Yes) - **Always include for correct decimal arithmetic**
130
+ **Class with local includes** (`.clas.locals_def.abap` exists — add `CLSCCINCL` before `FIXPT`):
81
131
 
82
- **Note**: `<FIXPT>X</FIXPT>` is default for modern ABAP. Without it, decimals treated as integers.
132
+ ```xml
133
+ <?xml version="1.0" encoding="utf-8"?>
134
+ <abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
135
+ <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
136
+ <asx:values>
137
+ <VSEOCLASS>
138
+ <CLSNAME>ZCL_MY_CLASS</CLSNAME>
139
+ <LANGU>E</LANGU>
140
+ <DESCRIPT>Description of the class</DESCRIPT>
141
+ <STATE>1</STATE>
142
+ <CLSCCINCL>X</CLSCCINCL>
143
+ <FIXPT>X</FIXPT>
144
+ <UNICODE>X</UNICODE>
145
+ <WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
146
+ </VSEOCLASS>
147
+ </asx:values>
148
+ </asx:abap>
149
+ </abapGit>
150
+ ```
83
151
 
84
- **Local Classes**: If the class has local classes (e.g., test doubles), add:
85
- - `<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>` - for test classes
86
- - `<CLSCCINCL>X</CLSCCINCL>` - for local class definitions
152
+ **Exception class** (`CATEGORY>40` add `CATEGORY` after `DESCRIPT`):
153
+
154
+ ```xml
155
+ <?xml version="1.0" encoding="utf-8"?>
156
+ <abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
157
+ <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
158
+ <asx:values>
159
+ <VSEOCLASS>
160
+ <CLSNAME>ZCX_MY_EXCEPTION</CLSNAME>
161
+ <LANGU>E</LANGU>
162
+ <DESCRIPT>My exception</DESCRIPT>
163
+ <CATEGORY>40</CATEGORY>
164
+ <STATE>1</STATE>
165
+ <CLSCCINCL>X</CLSCCINCL>
166
+ <FIXPT>X</FIXPT>
167
+ <UNICODE>X</UNICODE>
168
+ </VSEOCLASS>
169
+ </asx:values>
170
+ </asx:abap>
171
+ </abapGit>
172
+ ```
173
+
174
+ **Key rules**:
175
+ - ❌ **Never include `<EXPOSURE>`** in a CLAS XML — public (2) is the default and abapGit omits it
176
+ - ✅ Always include `<FIXPT>X</FIXPT>` and `<UNICODE>X</UNICODE>`
177
+ - ✅ Add `<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>` only when `.clas.testclasses.abap` exists
178
+ - ✅ Add `<CLSCCINCL>X</CLSCCINCL>` only when `.clas.locals_def.abap` exists
87
179
 
88
180
  **Local Class Files**:
89
181
  | File | Purpose |
@@ -98,7 +190,7 @@ abapGit needs XML files to:
98
190
  **Filename**: `src/zif_my_interface.intf.xml`
99
191
 
100
192
  ```xml
101
- <?xml version="1.0" encoding="utf-8"?>
193
+ <?xml version="1.0" encoding="utf-8"?>
102
194
  <abapGit version="v1.0.0" serializer="LCL_OBJECT_INTF" serializer_version="v1.0.0">
103
195
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
104
196
  <asx:values>
@@ -115,6 +207,9 @@ abapGit needs XML files to:
115
207
  </abapGit>
116
208
  ```
117
209
 
210
+ **Key rules**:
211
+ - ✅ `<EXPOSURE>2</EXPOSURE>` is **always present** for interfaces (unlike classes where it is omitted)
212
+
118
213
  ---
119
214
 
120
215
  ### Program (PROG)
@@ -122,13 +217,13 @@ abapGit needs XML files to:
122
217
  **Filename**: `src/zmy_program.prog.xml`
123
218
 
124
219
  ```xml
125
- <?xml version="1.0" encoding="utf-8"?>
220
+ <?xml version="1.0" encoding="utf-8"?>
126
221
  <abapGit version="v1.0.0" serializer="LCL_OBJECT_PROG" serializer_version="v1.0.0">
127
222
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
128
223
  <asx:values>
129
224
  <PROGDIR>
130
225
  <NAME>ZMY_PROGRAM</NAME>
131
- <SUBC>I</SUBC>
226
+ <SUBC>1</SUBC>
132
227
  <RLOAD>E</RLOAD>
133
228
  <FIXPT>X</FIXPT>
134
229
  <UCCHECK>X</UCCHECK>
@@ -140,8 +235,12 @@ abapGit needs XML files to:
140
235
 
141
236
  **Key Fields**:
142
237
  - `NAME`: Program name
143
- - `SUBC`: Subc (I = Include, 1 = Executable, F = Function Group, M = Module Pool, S = Subroutine Pool)
144
- - `RLOAD`: Rload (E = External, I = Internal)
238
+ - `SUBC`: Program type (`1`=Executable, `I`=Include, `F`=Function Group, `M`=Module Pool, `S`=Subroutine Pool)
239
+ - `RLOAD`: `E`=External
240
+ - `FIXPT`: Fixed-point arithmetic (`X`=Yes) — include for executables
241
+ - `UCCHECK`: Unicode checks active (`X`=Yes)
242
+
243
+ **Note**: The serializer may also write a `<TPOOL>` section after `<PROGDIR>` if the program has a title text. You do not need to write this when creating new programs — abapGit will add it on the next pull if needed.
145
244
 
146
245
  ---
147
246
 
@@ -149,8 +248,10 @@ abapGit needs XML files to:
149
248
 
150
249
  **Filename**: `src/zmy_table.tabl.xml`
151
250
 
251
+ > **XML-only object** — no `.abap` source file. Pull with: `pull --files src/zmy_table.tabl.xml`
252
+
152
253
  ```xml
153
- <?xml version="1.0" encoding="utf-8"?>
254
+ <?xml version="1.0" encoding="utf-8"?>
154
255
  <abapGit version="v1.0.0" serializer="LCL_OBJECT_TABL" serializer_version="v1.0.0">
155
256
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
156
257
  <asx:values>
@@ -168,9 +269,20 @@ abapGit needs XML files to:
168
269
 
169
270
  **Key Fields**:
170
271
  - `TABNAME`: Table name
171
- - `DDTEXT`: Description (NOT DESCRIPT)
172
- - `TABCLASS`: Table class (TRANSP = Transparent)
173
- - `CONTFLAG`: Delivery class (A = Application table)
272
+ - `DDTEXT`: Description (**not** `DESCRIPT`)
273
+ - `TABCLASS`: `TRANSP`=Transparent (most common), `POOL`, `CLUSTER`
274
+ - `CONTFLAG`: Delivery class — `A`=Application, `C`=Customizing, `S`=System, `G`=Customizing protected
275
+
276
+ **Note**: When abapGit serializes an existing table it also writes `<DD09L>` (technical settings) and `<DD03P_TABLE>` (field definitions). These sections are generated automatically from the ABAP Dictionary on pull — you only need the `<DD02V>` header when creating a new table. After the first pull the XML will be expanded with those sections.
277
+
278
+ **`DD03P` field-level rules** (apply when editing an existing table XML that includes `<DD03P_TABLE>`):
279
+
280
+ | Rule | Detail |
281
+ |---|---|
282
+ | `SHLPORIGIN` | Include `<SHLPORIGIN>D</SHLPORIGIN>` on fields where the Dictionary provides a value help (e.g. fields with a domain that has fixed values or a search help). Omit on fields with no value help. |
283
+ | Field order for raw-type fields | For fields with no `ROLLNAME` (raw type, e.g. `CHAR`, `NUMC`), the serializer writes `<MASK>` **before** `<DDTEXT>`. For fields with a `ROLLNAME`, only `ROLLNAME` appears (no `MASK` or `DDTEXT`). |
284
+
285
+ **Why this matters**: Missing `SHLPORIGIN` or wrong `MASK`/`DDTEXT` order causes a permanent diff between git and the system-serialized XML.
174
286
 
175
287
  ---
176
288
 
@@ -181,7 +293,7 @@ abapGit needs XML files to:
181
293
  The XML format is identical for both types — only `SOURCE_TYPE` differs:
182
294
 
183
295
  ```xml
184
- <?xml version="1.0" encoding="utf-8"?>
296
+ <?xml version="1.0" encoding="utf-8"?>
185
297
  <abapGit version="v1.0.0" serializer="LCL_OBJECT_DDLS" serializer_version="v1.0.0">
186
298
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
187
299
  <asx:values>
@@ -207,8 +319,10 @@ The XML format is identical for both types — only `SOURCE_TYPE` differs:
207
319
 
208
320
  **Filename**: `src/zmy_dtel.dtel.xml`
209
321
 
322
+ > **XML-only object** — no `.abap` source file. Pull with: `pull --files src/zmy_dtel.dtel.xml`
323
+
210
324
  ```xml
211
- <?xml version="1.0" encoding="utf-8"?>
325
+ <?xml version="1.0" encoding="utf-8"?>
212
326
  <abapGit version="v1.0.0" serializer="LCL_OBJECT_DTEL" serializer_version="v1.0.0">
213
327
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
214
328
  <asx:values>
@@ -217,9 +331,9 @@ The XML format is identical for both types — only `SOURCE_TYPE` differs:
217
331
  <DDLANGUAGE>E</DDLANGUAGE>
218
332
  <DDTEXT>Description of data element</DDTEXT>
219
333
  <REPTEXT>Description</REPTEXT>
220
- <SCRTEXT_S>Short</SCRTEXT_S>
221
- <SCRTEXT_M>Medium</SCRTEXT_M>
222
- <SCRTEXT_L>Long Description</SCRTEXT_L>
334
+ <SCRTEXT_S>Short text</SCRTEXT_S>
335
+ <SCRTEXT_M>Medium text</SCRTEXT_M>
336
+ <SCRTEXT_L>Long description text</SCRTEXT_L>
223
337
  <DTELMASTER>E</DTELMASTER>
224
338
  <DATATYPE>CHAR</DATATYPE>
225
339
  <LENG>000010</LENG>
@@ -231,9 +345,84 @@ The XML format is identical for both types — only `SOURCE_TYPE` differs:
231
345
 
232
346
  **Key Fields**:
233
347
  - `ROLLNAME`: Data element name
234
- - `DDTEXT`: Description (NOT DESCRIPT)
235
- - `DATATYPE`: Data type (CHAR, NUMC, etc.)
236
- - `LENG`: Length (e.g., 000010 for 10 characters)
348
+ - `DDTEXT`: Description (**not** `DESCRIPT`)
349
+ - `REPTEXT` / `SCRTEXT_S` / `SCRTEXT_M` / `SCRTEXT_L`: Field labels (report heading, short, medium, long)
350
+ - `DTELMASTER`: Language key for label master (`E` for English)
351
+ - `DATATYPE`: Data type (`CHAR`, `NUMC`, `DATS`, `TIMS`, `INT4`, etc.)
352
+ - `LENG`: Length padded to 6 digits (e.g. `000010` for 10 characters)
353
+
354
+ **Omit `<PARAMID>`** unless you specifically need a SET/GET parameter ID — the serializer omits it when empty, so including an empty tag causes a permanent diff.
355
+
356
+ ---
357
+
358
+ ### Structure (STRU)
359
+
360
+ **Filename**: `src/zmy_struct.stru.xml`
361
+
362
+ > **XML-only object** — no `.abap` source file. abapGit uses the same TABL serializer for STRU.
363
+ > Pull with: `pull --files src/zmy_struct.stru.xml`
364
+
365
+ ```xml
366
+ <?xml version="1.0" encoding="utf-8"?>
367
+ <abapGit version="v1.0.0" serializer="LCL_OBJECT_TABL" serializer_version="v1.0.0">
368
+ <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
369
+ <asx:values>
370
+ <DD02V>
371
+ <TABNAME>ZMY_STRUCT</TABNAME>
372
+ <DDLANGUAGE>E</DDLANGUAGE>
373
+ <TABCLASS>INTTAB</TABCLASS>
374
+ <DDTEXT>Description of the structure</DDTEXT>
375
+ <CONTFLAG>A</CONTFLAG>
376
+ </DD02V>
377
+ </asx:values>
378
+ </asx:abap>
379
+ </abapGit>
380
+ ```
381
+
382
+ **Key difference from TABL**: `TABCLASS` is `INTTAB` (internal table / structure) instead of `TRANSP`.
383
+
384
+ **Note**: Like TABL, after the first pull abapGit expands the XML with `<DD03P_TABLE>` field definitions. When creating a new structure you only need the `<DD02V>` header.
385
+
386
+ ---
387
+
388
+ ### Table Type (TTYP)
389
+
390
+ **Filename**: `src/zmy_ttyp.ttyp.xml`
391
+
392
+ > **XML-only object** — no `.abap` source file.
393
+ > Pull with: `pull --files src/zmy_ttyp.ttyp.xml`
394
+
395
+ ```xml
396
+ <?xml version="1.0" encoding="utf-8"?>
397
+ <abapGit version="v1.0.0" serializer="LCL_OBJECT_TTYP" serializer_version="v1.0.0">
398
+ <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
399
+ <asx:values>
400
+ <DD40V>
401
+ <TYPENAME>ZMY_TTYP</TYPENAME>
402
+ <DDLANGUAGE>E</DDLANGUAGE>
403
+ <DDTEXT>Description of table type</DDTEXT>
404
+ <ROWTYPE>ZMY_STRUCT</ROWTYPE>
405
+ <ROWKIND>S</ROWKIND>
406
+ <DATATYPE>TABLE_T</DATATYPE>
407
+ <ACCESSMODE>T</ACCESSMODE>
408
+ <KEYDEF>D</KEYDEF>
409
+ <KEYKIND>N</KEYKIND>
410
+ </DD40V>
411
+ </asx:values>
412
+ </asx:abap>
413
+ </abapGit>
414
+ ```
415
+
416
+ **Key Fields**:
417
+ - `TYPENAME`: Table type name
418
+ - `ROWTYPE`: Row type (a structure or data element name)
419
+ - `ROWKIND`: Row kind — `S`=Structure/Type, `D`=Data element, `R`=Reference
420
+ - `DATATYPE`: Always `TABLE_T` for table types
421
+ - `ACCESSMODE`: Table kind — `T`=Standard, `S`=Sorted, `H`=Hashed
422
+ - `KEYDEF`: Key definition — `D`=Default (standard key), `K`=User-defined
423
+ - `KEYKIND`: Key uniqueness — `N`=Non-unique, `U`=Unique
424
+
425
+ **Note**: After the first pull abapGit may expand the XML with `<DD42V>` (component definitions) if key fields are explicitly defined.
237
426
 
238
427
  ---
239
428
 
@@ -12,14 +12,23 @@ grand_parent: ABAP Development
12
12
 
13
13
  **Each ABAP object requires an XML metadata file for abapGit to understand how to handle it.**
14
14
 
15
- | Object Type | ABAP File (if folder=/src/) | XML File | Details |
16
- |-------------|------------------------------|----------|---------|
17
- | Class | `src/zcl_*.clas.abap` | `src/zcl_*.clas.xml` | See `guidelines/abapgit.md` |
18
- | Interface | `src/zif_*.intf.abap` | `src/zif_*.intf.xml` | See `guidelines/abapgit.md` |
19
- | Program | `src/z*.prog.abap` | `src/z*.prog.xml` | See `guidelines/abapgit.md` |
20
- | Table | `src/z*.tabl.abap` | `src/z*.tabl.xml` | See `guidelines/abapgit.md` |
21
- | **CDS View Entity** | `src/zc_*.ddls.asddls` | `src/zc_*.ddls.xml` | **Use by default** - See `guidelines/cds.md` |
22
- | CDS View (legacy) | `src/zc_*.ddls.asddls` | `src/zc_*.ddls.xml` | Only if explicitly requested - See `guidelines/cds.md` |
15
+ Replace `<name>` with the actual object name from this project's naming conventions
16
+ (`guidelines/objects.local.md`, or `guidelines/objects.md` as fallback).
17
+
18
+ | Object Type | ABAP Source File | XML File | Details |
19
+ |-------------|-----------------|----------|---------|
20
+ | Class | `<name>.clas.abap` | `<name>.clas.xml` | See `guidelines/abapgit.md` |
21
+ | Interface | `<name>.intf.abap` | `<name>.intf.xml` | See `guidelines/abapgit.md` |
22
+ | Program | `<name>.prog.abap` | `<name>.prog.xml` | See `guidelines/abapgit.md` |
23
+ | **CDS View Entity** | `<name>.ddls.asddls` | `<name>.ddls.xml` | **Use by default** - See `guidelines/cds.md` |
24
+ | CDS View (legacy) | `<name>.ddls.asddls` | `<name>.ddls.xml` | Only if explicitly requested - See `guidelines/cds.md` |
25
+ | Table (TABL) | *(none — XML-only)* | `<name>.tabl.xml` | See `guidelines/abapgit.md` |
26
+ | Structure (STRU) | *(none — XML-only)* | `<name>.stru.xml` | See `guidelines/abapgit.md` |
27
+ | Data Element (DTEL) | *(none — XML-only)* | `<name>.dtel.xml` | See `guidelines/abapgit.md` |
28
+ | Table Type (TTYP) | *(none — XML-only)* | `<name>.ttyp.xml` | See `guidelines/abapgit.md` |
29
+
30
+ > **XML-only objects (TABL, STRU, DTEL, TTYP)**: create only the `.xml` file — there is no `.abap` source file.
31
+ > After committing and pushing, pull with: `pull --files <folder>/<name>.tabl.xml --sync-xml`
23
32
 
24
33
  **IMPORTANT: When user says "create CDS view", create CDS View Entity by default.**
25
34
 
@@ -37,14 +46,14 @@ When a class needs local helper classes or test doubles, use separate files:
37
46
 
38
47
  | File | Purpose |
39
48
  |------|---------|
40
- | `zcl_xxx.clas.locals_def.abap` | Local class definitions |
41
- | `zcl_xxx.clas.locals_imp.abap` | Local class implementations |
49
+ | `<name>.clas.locals_def.abap` | Local class definitions |
50
+ | `<name>.clas.locals_imp.abap` | Local class implementations |
42
51
 
43
52
  **XML Configuration**: Add `<CLSCCINCL>X</CLSCCINCL>` to the class XML to include local class definitions:
44
53
 
45
54
  ```xml
46
55
  <VSEOCLASS>
47
- <CLSNAME>ZCL_XXX</CLSNAME>
56
+ <CLSNAME>MY_CLASS_NAME</CLSNAME>
48
57
  ...
49
58
  <CLSCCINCL>X</CLSCCINCL>
50
59
  </VSEOCLASS>
@@ -8,67 +8,78 @@ grand_parent: ABAP Development
8
8
 
9
9
  # ABAP Object Naming Conventions
10
10
 
11
- > **Project-specific overrides**: Add your naming conventions to `guidelines/objects.local.md`.
12
- > That file is never overwritten by `abapgit-agent init --update` and is searched by the `ref` command.
11
+ **Searchable keywords**: naming convention, prefix, namespace, object type, CLAS, INTF, PROG, TABL, DDLS, SAP namespace, customer namespace, PoC, probe
13
12
 
14
- **Searchable keywords**: naming convention, Z prefix, namespace, object type, CLAS, INTF, PROG, TABL, DDLS
13
+ ## SAP Namespace vs Customer Namespace
15
14
 
16
- ## TOPICS IN THIS FILE
17
- 1. Naming Conventions
18
- 2. ABAP Object Types
19
- 3. XML Metadata (see guidelines/abapgit.md)
15
+ | | SAP Namespace | Customer Namespace |
16
+ |---|---|---|
17
+ | **Object prefix** | `CL_*`, `IF_*`, `/NAME/CL_*`, `/NAME/IF_*`, etc. | `Z*`, `Y*` |
18
+ | **Package prefix** | SAP-delivered (e.g. `SFIN`, `CA_*`, `/NAME/*`) | `Z*`, `Y*`, `$*` |
19
+ | **Ownership** | Delivered and maintained by SAP | Owned by the customer |
20
+ | **In git repo** | Objects from an SAP-delivered package | Custom development objects |
20
21
 
21
- ## Naming Conventions
22
+ > **Rule**: Never add customer-created objects (including PoC/probe classes) into SAP namespace
23
+ > packages. PoC objects always use `Z*`/`Y*` prefix and always go in a `Z*`, `Y*`, or `$*` package
24
+ > — even on a project where production objects use SAP namespace.
22
25
 
23
- Use `Z_` or `Y_` prefix for custom objects:
26
+ ## How to Determine This Project's Naming Convention
24
27
 
25
- | Object Type | Prefix | Example |
26
- |-------------|--------|---------|
27
- | Class | ZCL_ | ZCL_MY_CLASS |
28
- | Interface | ZIF_ | ZIF_MY_INTERFACE |
29
- | Program | Z | ZMY_PROGRAM |
30
- | Package | $ | $MY_PACKAGE |
31
- | Table | Z | ZMY_TABLE |
32
- | CDS View | ZC | ZC_MY_VIEW |
33
- | CDS Entity | ZE | ZE_MY_ENTITY |
34
- | Data Element | Z | ZMY_ELEMENT |
35
- | Structure | Z | ZMY_STRUCTURE |
36
- | Table Type | Z | ZMY_TABLE_TYPE |
28
+ ```
29
+ 1. Check guidelines/objects.local.md ← this project's actual conventions (always check first)
30
+ 2. No objects.local.md exists? ← assume customer namespace project, use Z/Y defaults below
31
+ ```
37
32
 
38
- ## ABAP Object Types
33
+ `objects.local.md` is never overwritten by `abapgit-agent init --update`. It specifies the
34
+ naming pattern for production objects in this project — which could be customer namespace
35
+ (`ZCL_*`, `YCL_*`) or SAP namespace (`CL_*`, `/NAMESPACE/CL_*`).
39
36
 
40
- Common object types in this project:
37
+ ## Default Naming Conventions (Z/Y customer namespace)
41
38
 
42
- | Type | Description | File Suffix |
43
- |------|-------------|-------------|
44
- | CLAS | Classes | .clas.abap |
45
- | PROG | Programs | .prog.abap |
46
- | FUGR | Function Groups | .fugr.abap |
47
- | INTF | Interfaces | .intf.abap |
48
- | TABL | Tables | .tabl.abap |
49
- | STRU | Structures | .stru.abap |
50
- | DTEL | Data Elements | .dtel.abap |
51
- | TTYP | Table Types | .ttyp.abap |
52
- | DDLS | CDS Views | .ddls.asddls |
53
- | DDLX | CDS Entities | .ddlx.asddlx |
39
+ Applied when no `objects.local.md` exists:
54
40
 
55
- ## XML Metadata
41
+ | Object Type | Default Prefix | Default Example |
42
+ |-------------|---------------|-----------------|
43
+ | Class | `ZCL_` | `ZCL_MY_CLASS` |
44
+ | Interface | `ZIF_` | `ZIF_MY_INTERFACE` |
45
+ | Program | `Z` | `ZMY_PROGRAM` |
46
+ | Package | `$` | `$MY_PACKAGE` |
47
+ | Table | `Z` | `ZMY_TABLE` |
48
+ | CDS View Entity | `ZC_` | `ZC_MY_VIEW` |
49
+ | Data Element | `Z` | `ZMY_ELEMENT` |
50
+ | Structure | `Z` | `ZMY_STRUCTURE` |
51
+ | Table Type | `Z` | `ZMY_TABLE_TYPE` |
56
52
 
57
- **See guidelines/abapgit.md for XML templates.**
53
+ ## Project-Specific Conventions (`objects.local.md`)
58
54
 
59
- Each ABAP object requires an XML metadata file for abapGit. Quick reference:
55
+ `objects.local.md` should define both the naming prefix **and the correct package(s)** for
56
+ new objects. When package rules are present, Claude uses them directly without asking. When
57
+ absent, Claude must ask the user which package to use.
58
+
59
+ Examples of what `objects.local.md` might contain:
60
60
 
61
61
  ```
62
- Class: zcl_*.clas.xml
63
- Interface: zif_*.intf.xml
64
- Table: z*.tabl.xml
65
- CDS View: zc_*.ddls.xml
66
- ```
62
+ # Customer namespace — Y prefix
63
+ Class prefix: YCL_ e.g. YCL_MY_CLASS
64
+ Interface prefix: YIF_ e.g. YIF_MY_INTERFACE
65
+ Default package: YMYPROJECT
67
66
 
68
- **Important**: Always push to git BEFORE running pull:
69
- ```bash
70
- git add .
71
- git commit -m "Changes"
72
- git push # CRITICAL: Push FIRST
73
- abapgit-agent pull --files abap/zcl_my_class.clas.abap
67
+ # SAP namespace project single package
68
+ Class prefix: CL_ e.g. CL_MY_CLASS
69
+ Interface prefix: IF_ e.g. IF_MY_INTERFACE
70
+ Default package: MY_PACKAGE
71
+
72
+ # SAP namespace project — multiple packages
73
+ Class prefix: CL_ e.g. CL_MY_CLASS
74
+ Interface prefix: IF_ e.g. IF_MY_INTERFACE
75
+ Packages:
76
+ - Feature A objects → MY_PACKAGE_A
77
+ - Feature B objects → MY_PACKAGE_B
78
+
79
+ # SAP registered namespace
80
+ Class prefix: /MYNAMESPACE/CL_
81
+ Default package: /MYNAMESPACE/MAIN
74
82
  ```
83
+
84
+ → For file structure (what files to create): `abapgit-agent ref --topic object-creation`
85
+ → For XML templates: `abapgit-agent ref --topic abapgit`
@@ -0,0 +1,146 @@
1
+ ---
2
+ layout: default
3
+ title: Probe and PoC Guide
4
+ nav_order: 19
5
+ parent: ABAP Coding Guidelines
6
+ grand_parent: ABAP Development
7
+ ---
8
+
9
+ # Probe and PoC — Full Decision Flow
10
+
11
+ **Searchable keywords**: probe, PoC, proof of concept, scratchWorkspace, pocWorkspace,
12
+ disableProbeClasses, SAP namespace, customer namespace, throwaway class
13
+
14
+ ## Probe vs PoC — Key Difference
15
+
16
+ | | Probe | PoC |
17
+ |---|---|---|
18
+ | Intent | Throwaway — run once to test something | Validate an idea — may persist, needs proper home |
19
+ | Naming | Auto-derived (`ZCL_{USER}_*`) | Developer chooses a meaningful name |
20
+ | Location | `scratchWorkspace` (separate repo) | `pocWorkspace` (separate repo, manually set up) |
21
+ | Config | `scratchWorkspace` in `.abapGitAgent` | `pocWorkspace` in `.abapGitAgent` |
22
+
23
+ Both always use `Z*`/`Y*` prefix and a customer namespace package (`Z*`, `Y*`, `$*`).
24
+ Never use SAP namespace prefix or SAP-delivered package for probe/PoC objects.
25
+
26
+ ---
27
+
28
+ ## How to Determine the Project Namespace
29
+
30
+ Before applying the decision flow, determine the project namespace from `objects.local.md`:
31
+
32
+ ```
33
+ objects.local.md shows Z*/Y* prefix OR no objects.local.md exists
34
+ → Customer namespace project
35
+
36
+ objects.local.md shows CL_*, IF_*, /NAMESPACE/* prefix
37
+ → SAP namespace project
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Full Decision Flow
43
+
44
+ ```
45
+ User asks to create an object
46
+
47
+ ├── SAP namespace project AND user provides a Z*/Y* name
48
+ │ └── Ask: "Production objects here use CL_* prefix. Did you mean CL_MY_HELPER?
49
+ │ Or is this a PoC/probe object?"
50
+ │ ├── "PoC/probe" → continue as PoC or probe flow below
51
+ │ └── "Production" → correct the name to match project prefix, then proceed
52
+
53
+ ├── User confirms PROBE
54
+ │ ├── Customer namespace project
55
+ │ │ ├── disableProbeClasses = false / not set
56
+ │ │ │ └── Create in current project (Z*/Y* prefix, Z*/Y*/$* package)
57
+ │ │ └── disableProbeClasses = true
58
+ │ │ ├── scratchWorkspace configured → create there (see workflow below)
59
+ │ │ └── scratchWorkspace NOT configured
60
+ │ │ └── STOP — tell user to configure scratchWorkspace in .abapGitAgent
61
+ │ │
62
+ │ └── SAP namespace project
63
+ │ ├── scratchWorkspace configured → create there (see workflow below)
64
+ │ └── scratchWorkspace NOT configured
65
+ │ └── STOP — tell user to configure scratchWorkspace in .abapGitAgent
66
+ │ (disableProbeClasses is irrelevant for SAP namespace projects —
67
+ │ they have no customer namespace packages to host probes)
68
+
69
+ └── User confirms POC
70
+ ├── pocWorkspace.path configured in .abapGitAgent
71
+ │ └── Read {pocWorkspace.path}/.abapGitAgent → get package property
72
+ │ Show confirmation:
73
+ │ "I'll create this PoC object in:
74
+ │ Repo: {pocWorkspace.path}
75
+ │ Package: {package from that repo's .abapGitAgent}
76
+ │ Is this correct, or do you want a different PoC repo?"
77
+ │ ├── Confirmed → work in that repo using its naming/package rules
78
+ │ └── Different repo → ask user for the path, then confirm again
79
+ └── pocWorkspace.path NOT configured
80
+ └── STOP — tell user to set up a PoC repo first (see setup below)
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Probe Workflow (scratchWorkspace)
86
+
87
+ **Naming** — derive from `scratchWorkspace` config in `.abapGitAgent`:
88
+ - `classPrefix` (default: `ZCL_{USER}_`) + `<PURPOSE>`, max 30 chars
89
+ - Example: user=`JOHN`, purpose=`OPEN_TRANSPORTS` → `ZCL_JOHN_OPEN_TRANSPORTS`
90
+ - If name already exists in `{path}/src/`, append `_2`, `_3`, etc.
91
+
92
+ **Workflow:**
93
+ 1. Read `{scratchWorkspace.path}/.abapGitAgent` to confirm `folder` property (e.g. `/src/`)
94
+ 2. Write class files in `{path}/src/`
95
+ 3. Commit and push from `{path}`:
96
+ ```bash
97
+ cd {path} && git add . && git commit -m "probe: <description>" && git push
98
+ ```
99
+ 4. Activate:
100
+ ```bash
101
+ cd {path} && abapgit-agent pull --files src/<classname>.clas.abap
102
+ ```
103
+ 5. Tell user (do NOT auto-run):
104
+ ```
105
+ Class activated. Run with: abapgit-agent run --class <CLASSNAME>
106
+ ```
107
+ Run the command from the original project directory, not `{path}`.
108
+
109
+ ---
110
+
111
+ ## PoC Workflow (pocWorkspace)
112
+
113
+ Once the user confirms the repo and package:
114
+
115
+ 1. Read `{pocWorkspace.path}/.abapGitAgent` → get `folder` and `package`
116
+ 2. Read `{pocWorkspace.path}/guidelines/objects.local.md` if it exists → use its naming rules
117
+ 3. Write object files in `{pocWorkspace.path}/{folder}/`
118
+ 4. Commit and push from `{pocWorkspace.path}`:
119
+ ```bash
120
+ cd {pocWorkspace.path} && git add . && git commit -m "poc: <description>" && git push
121
+ ```
122
+ 5. Activate:
123
+ ```bash
124
+ cd {pocWorkspace.path} && abapgit-agent pull --files {folder}/<name>.<ext> --sync-xml
125
+ ```
126
+
127
+ ---
128
+
129
+ ## PoC Repo Setup (one-time, manual)
130
+
131
+ When `pocWorkspace.path` is not configured, tell the user to:
132
+
133
+ ```
134
+ 1. Create a git repo for the PoC:
135
+ mkdir my-poc && cd my-poc && git init && git remote add origin <url>
136
+
137
+ 2. Link it to an ABAP package:
138
+ abapgit-agent init --package <POC_PACKAGE>
139
+
140
+ 3. Add pocWorkspace to .abapGitAgent in the main project:
141
+ "pocWorkspace": { "path": "/absolute/path/to/my-poc" }
142
+ ```
143
+
144
+ **Switching PoCs**: update `pocWorkspace.path` to point to the new PoC repo.
145
+ Claude always shows the current repo and package before creating anything —
146
+ a stale path is caught before any files are written.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abapgit-agent",
3
- "version": "1.13.2",
3
+ "version": "1.13.4",
4
4
  "description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
5
5
  "files": [
6
6
  "bin/",
@@ -27,6 +27,8 @@
27
27
  "test:cmd:demo": "node tests/run-all.js --cmd --demo",
28
28
  "test:cmd:syntax": "node tests/run-all.js --cmd --command=syntax",
29
29
  "test:cmd:pull": "node tests/run-all.js --cmd --command=pull",
30
+ "test:sync-xml": "node tests/run-all.js --sync-xml",
31
+ "test:xml-only": "node tests/run-all.js --xml-only",
30
32
  "test:cmd:inspect": "node tests/run-all.js --cmd --command=inspect",
31
33
  "test:cmd:unit": "node tests/run-all.js --cmd --command=unit",
32
34
  "test:cmd:view": "node tests/run-all.js --cmd --command=view",
@@ -5,6 +5,7 @@
5
5
  const { printHttpError } = require('../utils/format-error');
6
6
  const fs = require('fs');
7
7
  const pathModule = require('path');
8
+ const { execSync } = require('child_process');
8
9
 
9
10
  // Calculate display width accounting for emoji (2 cells) vs ASCII (1 cell)
10
11
  function calcWidth(str) {
@@ -42,6 +43,7 @@ module.exports = {
42
43
  async execute(args, context) {
43
44
  const { loadConfig, AbapHttp, gitUtils, getTransport, getSafeguards, getConflictSettings, getTransportSettings } = context;
44
45
  const verbose = args.includes('--verbose');
46
+ const syncXml = args.includes('--sync-xml');
45
47
 
46
48
  // Check project-level safeguards
47
49
  const safeguards = getSafeguards();
@@ -96,21 +98,31 @@ module.exports = {
96
98
  if (filesArgIndex !== -1 && filesArgIndex + 1 < args.length) {
97
99
  files = args[filesArgIndex + 1].split(',').map(f => f.trim());
98
100
 
99
- // Validate that every file has a recognised ABAP source extension
100
- // (.abap or .asddls) XML metadata files must NOT be passed here
101
+ // Validate that every file has a recognised extension:
102
+ // - .abap / .asddls ABAP source files
103
+ // - name.type.xml — XML-only objects (TABL, STRU, DTEL, TTYP, ...)
104
+ // Must have exactly 3 dot-separated parts with a non-empty name part
105
+ // to exclude .abapgit.xml, package.devc.xml, plain .xml, etc.
101
106
  const ABAP_SOURCE_EXTS = new Set(['abap', 'asddls']);
107
+ const isXmlOnlyObject = (f) => {
108
+ const base = f.split('/').pop();
109
+ const parts = base.split('.');
110
+ return parts.length === 3 && parts[0].length > 0 && parts[2].toLowerCase() === 'xml';
111
+ };
102
112
  const nonSourceFiles = files.filter(f => {
103
113
  const base = f.split('/').pop(); // strip directory
104
114
  const parts = base.split('.');
105
115
  const ext = parts[parts.length - 1].toLowerCase();
106
- return parts.length < 3 || !ABAP_SOURCE_EXTS.has(ext);
116
+ if (ABAP_SOURCE_EXTS.has(ext)) return false; // .abap / .asddls — valid
117
+ if (isXmlOnlyObject(f)) return false; // name.type.xml — valid
118
+ return true; // everything else — invalid
107
119
  });
108
120
  if (nonSourceFiles.length > 0) {
109
- console.error('❌ Error: --files only accepts ABAP source files (.abap, .asddls).');
110
- console.error(' The following file(s) are not ABAP source files:');
121
+ console.error('❌ Error: --files only accepts ABAP source files (.abap, .asddls) or XML-only object files (name.type.xml).');
122
+ console.error(' The following file(s) are not recognised:');
111
123
  nonSourceFiles.forEach(f => console.error(` ${f}`));
112
- console.error(' Tip: pass the source file, not the XML metadata file.');
113
- console.error(' Example: --files src/zcl_my_class.clas.abap');
124
+ console.error(' Tip: for source objects pass the .abap file; for XML-only objects (TABL, STRU, DTEL, TTYP)');
125
+ console.error(' pass the .xml file, e.g. --files abap/ztable.tabl.xml');
114
126
  process.exit(1);
115
127
  }
116
128
 
@@ -198,13 +210,13 @@ module.exports = {
198
210
  }
199
211
  }
200
212
 
201
- await this.pull(gitUrl, branch, files, transportRequest, loadConfig, AbapHttp, jsonOutput, undefined, conflictMode, verbose);
213
+ await this.pull(gitUrl, branch, files, transportRequest, loadConfig, AbapHttp, jsonOutput, undefined, conflictMode, verbose, syncXml);
202
214
  },
203
215
 
204
- async pull(gitUrl, branch = 'main', files = null, transportRequest = null, loadConfig, AbapHttp, jsonOutput = false, gitCredentials = undefined, conflictMode = 'abort', verbose = false) {
216
+ async pull(gitUrl, branch = 'main', files = null, transportRequest = null, loadConfig, AbapHttp, jsonOutput = false, gitCredentials = undefined, conflictMode = 'abort', verbose = false, syncXml = false, isRepull = false) {
205
217
  const TERM_WIDTH = process.stdout.columns || 80;
206
218
 
207
- if (!jsonOutput) {
219
+ if (!jsonOutput && !isRepull) {
208
220
  console.log(`\n🚀 Starting pull for: ${gitUrl}`);
209
221
  console.log(` Branch: ${branch}`);
210
222
  if (files && files.length > 0) {
@@ -438,6 +450,71 @@ module.exports = {
438
450
  throw err;
439
451
  }
440
452
 
453
+ // --- Post-pull XML sync ---
454
+ // abapGit's status calculation already identified which XML files differ
455
+ // (match=false) — only those are returned in local_xml_files.
456
+ const localXmlFiles = result.local_xml_files || result.LOCAL_XML_FILES || [];
457
+
458
+ if (localXmlFiles.length > 0) {
459
+ const diffFiles = [];
460
+ for (const f of localXmlFiles) {
461
+ const relPath = ((f.path || f.PATH || '') + (f.filename || f.FILENAME || '')).replace(/^\//, '');
462
+ const absPath = pathModule.join(process.cwd(), relPath);
463
+ if (!fs.existsSync(absPath)) continue;
464
+ const incoming = Buffer.from(f.data || f.DATA, 'base64');
465
+ // Double-check: only write if bytes actually differ (guard against encoding quirks)
466
+ const current = fs.readFileSync(absPath);
467
+ if (!current.equals(incoming)) {
468
+ diffFiles.push({ relPath, absPath, incoming });
469
+ }
470
+ }
471
+
472
+ if (diffFiles.length > 0 && !syncXml) {
473
+ console.log(`\n⚠️ ${diffFiles.length} XML file(s) differ from serializer output:`);
474
+ for (const f of diffFiles) console.log(` ${f.relPath}`);
475
+ console.log(` Run with --sync-xml to accept serializer output and amend the last commit`);
476
+ } else if (diffFiles.length > 0 && syncXml) {
477
+ console.log(`\n🔄 Syncing ${diffFiles.length} XML file(s) to match serializer output:`);
478
+ for (const f of diffFiles) console.log(` ${f.relPath}`);
479
+
480
+ // 1. Write serializer XML to disk
481
+ for (const f of diffFiles) fs.writeFileSync(f.absPath, f.incoming);
482
+
483
+ // 2. Stage changed XML files
484
+ const quotedPaths = diffFiles.map(f => `"${f.relPath}"`).join(' ');
485
+ execSync(`git add ${quotedPaths}`, { cwd: process.cwd() });
486
+
487
+ // 3. Amend last commit
488
+ execSync('git commit --amend --no-edit', { cwd: process.cwd() });
489
+
490
+ // 4. Push with force-with-lease; if no upstream, set it automatically
491
+ let pushed = false;
492
+ try {
493
+ execSync('git push --force-with-lease', { cwd: process.cwd(), stdio: 'pipe' });
494
+ pushed = true;
495
+ } catch (pushErr) {
496
+ const msg = (pushErr.stderr || pushErr.stdout || pushErr.message || '').toString();
497
+ if (msg.includes('no upstream branch') || msg.includes('has no upstream')) {
498
+ // Branch not yet pushed — set upstream and force push (amend requires force)
499
+ execSync(`git push --force-with-lease --set-upstream origin ${branch}`, { cwd: process.cwd(), stdio: 'pipe' });
500
+ pushed = true;
501
+ }
502
+ // Any other push error (no remote at all, auth failure, etc.) → skip silently
503
+ }
504
+
505
+ if (pushed) {
506
+ console.log(` Re-pulling so ABAP system matches the amended commit...`);
507
+ // 5. Re-pull so ABAP system matches the amended commit (no sync loop)
508
+ await this.pull(gitUrl, branch, files, transportRequest, loadConfig, AbapHttp, jsonOutput, gitCredentials, conflictMode, verbose, false, true);
509
+ console.log(`\n✅ Synced ${diffFiles.length} XML file(s), amended commit, re-pulled`);
510
+ } else {
511
+ // No remote at all — files are written and committed locally
512
+ console.log(`\n✅ Synced ${diffFiles.length} XML file(s), amended commit`);
513
+ console.log(` Push skipped (no remote). Push manually to sync with remote.`);
514
+ }
515
+ }
516
+ }
517
+
441
518
  return result;
442
519
  } catch (error) {
443
520
  if (error._isPullError) {