Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ was deleted the same day.
| `shared/tables/required_authorizations.tsv` | sap-doctor (auth probe), docs/security.md (§1 mirror) | Per-capability required SAP authorizations (`capability · auth_object · field · values`). Rows sharing `(capability, auth_object)` form one AUTHORITY-CHECK group. Read by `sap-doctor/references/sap_doctor_authz_probe.ps1` (calls `SUSR_USER_AUTH_FOR_OBJ_GET` for the logged-in user). The comprehensive per-skill probe set — a large superset of the coarse 12-row core-developer summary in `docs/security.md §1` (which stays a human overview; this TSV carries the per-capability groups across all four plugins). |
| `shared/tables/perf_antipattern_map.tsv` | sap-trace | Maps a trace-detected performance signal to the matching ABAP code-quality rule + fix template. Read by `sap-trace/references/sap_trace.ps1`. |
| `shared/tables/sap_release_markers.tsv` | sap-login (via `sap_rfc_system_info.ps1`) | Release-marker lookup — maps (software component, release range) → the canonical `server_release_marker` stored on the connection profile; the input `sap_select_vbs_variant.ps1` scores VBS variants against. |
| `shared/scripts/sap_rfc_lib.ps1` | **All RFC-using PowerShell scripts (shared library)** | Reusable NCo 3.1 connect/disconnect helpers (`Connect-SapRfc`, `Disconnect-SapRfc`, `Add-RfcField`, `Add-RfcOption`). Dot-sourced via `%%RFC_LIB_PS1%%` token to keep the GAC discovery + `RfcConfigParameters` boilerplate in one place. |
| `shared/scripts/sap_rfc_lib.ps1` | **All RFC-using PowerShell scripts (shared library)** | Reusable NCo 3.1 connect/disconnect helpers (`Connect-SapRfc`, `Disconnect-SapRfc`, `Add-RfcField`, `Add-RfcOption`). Dot-sourced via `%%RFC_LIB_PS1%%` token to keep the GAC discovery + `RfcConfigParameters` boilerplate in one place. **Target stamp + assertion (2026-08-06, RFC side)**: every successful connect emits `RFC_TARGET: system=<SID> client=<NNN> user=<U> endpoint=<...> via=<pin\|gui-active\|default\|single-profile\|explicit-params>` — the sibling of the attach lib's `GUI_TARGET:` line — and when `SAPDEV_EXPECT_SYSTEM`/`_CLIENT` are exported (`Set-SapGuiTargetExpectation`; children inherit them) a mismatching resolution is a **hard refusal** (`ERROR` + `$null`, before logon on the profile path) instead of a silent wrong-system connect — closing the 2026-08-06 headless-child drift where a pinless session fell through to the saved DEFAULT profile. Identity compares the resolved profile's `system_name`/`client`, never `RfcDestination.SystemID` (configured R3NAME — blank on direct `-Server`/`-Sysnr` connections); caller-supplied endpoints are attributed via store endpoint-match, then live `SystemAttributes` best-effort. Deliberate cross-system legs (`/sap-compare`, `/sap-transport-sequencer`, `/sap-cc-*`) set no expectation and are unaffected. |
| `shared/scripts/sap_settings_lib.ps1` | **All PowerShell skill wrappers that need a userConfig value (mandatory — Rule 7)** | Settings reader/writer. Dot-source via `%%SETTINGS_LIB_PS1%%`. Functions: `Get-SapSettings` (returns merged object), `Get-SapSettingValue '<key>' '<default>'` (resolved string), `Set-SapUserSetting '<key>' '<value>'` (writes to `{work_dir}\runtime\userconfig.json`). Reads merge per-key on the `value` field: env `SAPDEV_AI_WORK_DIR` (work_dir only) > `settings.local.json` > `userconfig.json` > `settings.json`. All skill writes go to `userconfig.json` — never to `settings.json` or the hand-edited `settings.local.json`. |
| `shared/scripts/sap_tadir_delete.ps1` | sap-se21 (Step 8a), sap-dev-clean (Step 5) | **TADIR orphan cleanup ("P2" fix)** — deletes an orphaned object-directory row (definition gone, `TADIR` row survives, blocks the package delete) via the dev-init wrapper FM → `TR_TADIR_INTERFACE` (the SAP write API for TADIR; **not** remote-enabled, so it is reached through `Z_GENERIC_RFC_WRAPPER_TBL` as an asXML-serialized dynamic call — no raw SQL on TADIR). Forces `WI_TEST_MODUS=' '` (FM default is `'X'`=dry-run) + `WI_DELETE_TADIR_ENTRY='X'`. **Safety-guarded**: deletes a row ONLY when the object's definition is verifiably gone (DOMA→DD01L, DTEL→DD04L, TABL→DD02L, TTYP→DD40L, VIEW→DD25L, SHLP→DD30L, FUNC→TFDIR, FUGR→TLIBG, PROG/REPS→TRDIR; unmapped→`REFUSED_UNMAPPED`), `REFUSED_DEF_EXISTS` for a live object — so it can never orphan a live object. Authoritative success = a post-delete RFC re-read of TADIR returning zero rows (NOT the wrapper's echo). Args: `-Object/-ObjName` or `-Entries "OBJECT:NAME[,PGMID:OBJECT:NAME]"`, `-Force` (skip the def-gone guard), `-TestOnly` (classify only). 32-bit PS. Stdout: `TADIR: <DELETED\|WOULD_DELETE\|ALREADY_GONE\|REFUSED_DEF_EXISTS\|REFUSED_UNMAPPED\|FAILED> …` + `STATUS: OK deleted=<n> would=<w> gone=<g> refused=<r> failed=<f>`; exit 0/1/2. **Circular-teardown caveat**: cleaning the dev-init package's OWN orphans fails (the wrapper FM was deleted with it) — redeploy via `/sap-dev-init` or clean manually (SE03 / `RSWBO052`). |
| `shared/scripts/sap_rfc_connect.ps1` | sap-login | Standalone RFC connection probe (NCo 3.1) — thin wrapper around `sap_rfc_lib.ps1` |
Expand All @@ -347,7 +347,7 @@ was deleted the same day.
| `shared/scripts/sap_log_lib.vbs` | **All VBScript skill scripts (optional)** | Structured logger. Include via `ExecuteGlobal FSO.OpenTextFile("%%LOG_LIB_VBS%%",1).ReadAll()`. Functions: `LogStart(skill, paramsArray)`, `LogStep(runId, level, step, msg)`, `LogEnd(runId, status, exitCode, errorMsg)`. Same JSONL/TSV/TEXT formats and redaction as the PS lib. Writes UTF-8 (no BOM) via ADODB.Stream so files concatenate cleanly with PS-emitted lines. |
| `shared/scripts/sap_session_lock.vbs` | **All GUI-scripting VBS reference scripts that perform multi-step writes (mandatory per Rule 7)** | Session-lock helpers. Include via `ExecuteGlobal FSO.OpenTextFile("%%SESSION_LOCK_VBS%%",1).ReadAll()`. Functions: `TryLockSession(sess)` → returns Boolean (False if API unavailable on this SAP GUI build); `ReleaseSession(sess, wasLocked)` — idempotent unlock that ALSO sweeps up to 5 chained orphan modal popups via `sendVKey 12` (F12 / Cancel) before unlocking, so the user never gets a frozen popup on session handover. Wrap source-paste / save / activate / popup-driving critical sections to block in-session focus stealing. Pair with the existing AppActivate-loop guards for SendKeys-based pastes (defence in depth: AppActivate blocks external focus stealing, LockSessionUI blocks internal, the pre-unlock sweep covers leftover modals). |
| `shared/scripts/sap_delete_popups.vbs` | **The delete VBS of sap-se37 / sap-se11 / sap-se24 / sap-se38 / sap-se21** | Shared post-delete popup walker. Included by deriving its path from the already-substituted `%%ATTACH_LIB_VBS%%` token (same dir, so no extra generator token): `sDpDir = oDpFso.GetParentFolderName("%%ATTACH_LIB_VBS%%") : ExecuteGlobal oDpFso.OpenTextFile(oDpFso.BuildPath(sDpDir, "sap_delete_popups.vbs"),1).ReadAll()`. Exposes `Function WalkDeletePopups(oSession, objdirPkg, objdirLang, sapTr)` → walks the active window (cap 10), dispatching each modal by DDIC control id ONLY (locale-independent): SAPLSETX language (`ctxtRSETX-MASTERLANG`/`btnPUSH1`), KO007 "Create Object Directory Entry" (ECC6 — fill empty package from `objdirPkg` + 1-char `objdirLang`, else accept pre-filled, else Local Object `btn[7]`), TR prompt (`ctxtKO008-TRKORR`; returns `"ABORT_EMPTY_TR"` when `sapTr` is empty so the caller releases its lock + `WScript.Quit 1`), and a confirm cascade (`btnSPOP-OPTION1` / `btnBUTTON_1` / `tbar[0]/btn[0]` / Enter). Each branch is gated by its control id, so the union is a strict superset of every per-skill loop it replaced and cannot misfire on a screen lacking that control. Pure function library (receives an already-attached `oSession`; does NOT bind the Scripting engine / declare `SESSION_PATH` / include the attach lib / call `AttachSapSession`), so — like `sap_session_lock.vbs` — it is not a "driving" VBS and lives in `shared/scripts/`, outside the `skills/*/references/` scan scope of `scripts/check-consistency.mjs` (no baseline required). se19 (classic + new) and cmod keep their own popup handling (divergent `For pass` / sequential structure + lenient-TR semantics). |
| `shared/scripts/sap_attach_lib.vbs` | **All GUI-scripting VBS reference scripts that drive SAP GUI** — mandatory for the Tier 3 (parallel-safe session attach) contract. | Shared session-attach primitive — **multi-connection aware (Phase 3.5)** + **pin-file-free (Phase 4.2)**. Include via `ExecuteGlobal FSO.OpenTextFile("%%ATTACH_LIB_VBS%%",1).ReadAll()`. Exposes `Function AttachSapSession(sHint)` which resolves the target session in this order: (1) `sHint` (typically the `%%SESSION_PATH%%` token from the calling wrapper); (2) `SAPDEV_SESSION_PATH` env var — set by the SKILL.md wrapper to `Get-SapCurrentSessionPath`'s return; (3) sole-connection + sole-session safe default; (4) **refuse loud** with `ERROR: N SAP connections attached; cannot pick one safely. Run /sap-login to pin a connection, or pass --session ...`. **Strategies 1 and 2 also work cross-connection** — they take full `/app/con[N]/ses[M]` paths and never silently retarget. Strategies 3 and 4 keep single-connection callers simple while multi-connection callers get safe refusal instead of silent miss-targeting. The convention: each migrated VBS declares `Const SESSION_PATH = "%%SESSION_PATH%%"`, includes this lib, and calls `Set oSession = AttachSapSession(SESSION_PATH)`. Calling skill wrappers (PowerShell) substitute `%%SESSION_PATH%%` with the parsed `--session` argument (or empty), `%%ATTACH_LIB_VBS%%` with the absolute path to this file, AND set `$env:SAPDEV_SESSION_PATH = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'` (from `sap_connection_lib.ps1`) so the AI session's pin propagates. Unsubstituted-token sentinel via a `Chr(37)`-built runtime string so global wrapper substitution cannot corrupt the comparison. Pairs with the broker: the broker decides which session belongs to this AI session; the helper lets every VBS attach to that decision safely. **Target stamp + assertion (2026-08-06)**: every success path now echoes `GUI_TARGET: system=<SID> client=<NNN> user=<U> path=<...> via=<strategy>` (read from `GuiSession.Info` — IDs, not localised text), and when the wrapper exports `SAPDEV_EXPECT_SYSTEM` / `SAPDEV_EXPECT_CLIENT` a mismatch is a **hard refusal** (exit 2) instead of a silent retarget. Set them via `Set-SapGuiTargetExpectation` (`sap_connection_lib.ps1`), which resolves the *same* profile `Connect-SapRfc` picks — so the GUI leg and the RFC leg of a skill can no longer land on different SAP systems. **Both must be exported in the same process that launches `cscript`** (env vars die with the generator block). Unset = legacy behaviour, still stamped. |
| `shared/scripts/sap_attach_lib.vbs` | **All GUI-scripting VBS reference scripts that drive SAP GUI** — mandatory for the Tier 3 (parallel-safe session attach) contract. | Shared session-attach primitive — **multi-connection aware (Phase 3.5)** + **pin-file-free (Phase 4.2)**. Include via `ExecuteGlobal FSO.OpenTextFile("%%ATTACH_LIB_VBS%%",1).ReadAll()`. Exposes `Function AttachSapSession(sHint)` which resolves the target session in this order: (1) `sHint` (typically the `%%SESSION_PATH%%` token from the calling wrapper); (2) `SAPDEV_SESSION_PATH` env var — set by the SKILL.md wrapper to `Get-SapCurrentSessionPath`'s return; (3) sole-connection + sole-session safe default; (4) **refuse loud** with `ERROR: N SAP connections attached; cannot pick one safely. Run /sap-login to pin a connection, or pass --session ...`. **Strategies 1 and 2 also work cross-connection** — they take full `/app/con[N]/ses[M]` paths and never silently retarget. Strategies 3 and 4 keep single-connection callers simple while multi-connection callers get safe refusal instead of silent miss-targeting. The convention: each migrated VBS declares `Const SESSION_PATH = "%%SESSION_PATH%%"`, includes this lib, and calls `Set oSession = AttachSapSession(SESSION_PATH)`. Calling skill wrappers (PowerShell) substitute `%%SESSION_PATH%%` with the parsed `--session` argument (or empty), `%%ATTACH_LIB_VBS%%` with the absolute path to this file, AND set `$env:SAPDEV_SESSION_PATH = Get-SapCurrentSessionPath -WorkTemp '{WORK_TEMP}'` (from `sap_connection_lib.ps1`) so the AI session's pin propagates. Unsubstituted-token sentinel via a `Chr(37)`-built runtime string so global wrapper substitution cannot corrupt the comparison. Pairs with the broker: the broker decides which session belongs to this AI session; the helper lets every VBS attach to that decision safely. **Target stamp + assertion (2026-08-06)**: every success path now echoes `GUI_TARGET: system=<SID> client=<NNN> user=<U> path=<...> via=<strategy>` (read from `GuiSession.Info` — IDs, not localised text), and when the wrapper exports `SAPDEV_EXPECT_SYSTEM` / `SAPDEV_EXPECT_CLIENT` a mismatch is a **hard refusal** (exit 2) instead of a silent retarget. Set them via `Set-SapGuiTargetExpectation` (`sap_connection_lib.ps1`), which resolves the *same* profile `Connect-SapRfc` picks — so the GUI leg and the RFC leg of a skill can no longer land on different SAP systems (since 2026-08-06 `Connect-SapRfc` enforces the same pair itself and stamps `RFC_TARGET:` — see the `sap_rfc_lib.ps1` row). **Both must be exported in the same process that launches `cscript`** (env vars die with the generator block). Unset = legacy behaviour, still stamped. |
| `shared/scripts/sap_session_broker.ps1` | **All GUI-scripting skills that may run in parallel** (today: `/sap-gui-skill-scaffold` parallel path + the 4 Phase-3.1 migrated read-only skills; will become broadly mandatory after Tier 3 migration). Full contract: `shared/rules/sap_session_broker.md`. | SAP GUI Session Broker — **multi-connection aware (v2 schema, Phase 3.5)**. PowerShell, ~600 LOC. Single-binary CLI with five actions — `acquire` / `release` / `discover` / `gc` / `list` — driven by `-Action <name> -WorkTemp <abs-path>` + per-action args. State lives in `{WORK_TEMP}\session_registry.json` (UTF-8 no BOM, nested `connections[]` shape); cross-process concurrency serialized by a named Windows mutex (`SapDevSessionBroker_v2`) acquired through `System.Threading.Mutex` with a 10s timeout for crash recovery. **Connection isolation**: a claim resolved against connection N never returns a session of connection M. Reactive cleanup + identity-reconciliation sweep runs inside every acquire/release/discover/gc across ALL connections: it mirrors live SAP identity onto each block (live is source of truth) then drops entries on these failure modes — session closed, owner PID dead, TTL expired, relogin, entire connection closed. A reused `/app/con[N]` slot now hosting a DIFFERENT system is detected by the `(system,client,user)` tuple — NOT `SystemSessionId`, which on the tested kernels is per-workstation, not per-logon, and stays identical across an A→B swap on one slot (the 2026-06-07 stale-identity bug); on a tuple change the block is reset to the live identity and its stale `connection_id` cleared (re-bound on next finalize). Idempotent on re-acquire by `task_id`. Connection-targeting acquire args (Phase 4.1+): broker auto-resolves `-AiSessionId` via parent-PID walk and reads its `ai_sessions[<id>].connection_id` pin. Explicit `-SessionPath` / `-ConnectionPath` / `-SystemName -Client -User` still override; resolution falls through to sole-connection auto-default or DENIED. Spawns on demand on the target connection via `/oSESSION_MANAGER` (the only OK-code mechanism verified on S/4HANA 1909 kernel 754; `CreateSession` and bare `/o` no-op). Stdout last line: `ACQUIRED: path=<p> sessionNumber=<n> connection=<c> reused=<bool>` / `RELEASED: path=<p> connection=<c>` / `NOT_FOUND` / `DENIED: <reason>` (exit 1) / `ERROR: <reason>` (exit 2). Auto-rebuilds a v1 registry on first call after upgrade with a `WARN: v1 registry detected` line. Shells out to `sap_session_broker_com.vbs` for every SAP-side operation because PowerShell 7+/.NET 5+ cannot bind the SAP GUI Scripting Engine directly (`Marshal::GetActiveObject` removed in .NET 5+; even 32-bit Windows PowerShell 5.1 fails to resolve the SAPGUI ProgID through the ROT). |
| `shared/scripts/sap_session_broker_com.vbs` | **Internal helper for `sap_session_broker.ps1`** — not intended for direct calls by other skills. | SAP COM helper for the broker. VBScript run via 32-bit `cscript`. Single argv command + JSON-on-stdout protocol. **Multi-connection aware** (Phase 3.5): `INFO` returns ALL attached SAP connections (each with `connection_path` / `description` / `system_name` / `client` / `user` / `language` / `logon_id` + a `sessions[]` array); `SPAWN <connection_path>` spawns on a SPECIFIC connection (drives `/n` + `/oSESSION_MANAGER` on that connection's anchor, returns the newcomer's path + `SessionNumber`); `RESET <session_path>` drives `/n` on a specific session (used by `release` to return to SAP Easy Access); `PROBE <session_path>` does a single-session `findById` + `Info` read (used by acquire's pre-allocation Easy-Access verification). Exit codes: 0 success, 1 usage error, 2 SAP-unreachable, 3 command-level failure (details in JSON `error` field). JSON output is one line per invocation — broker parses with `ConvertFrom-Json`. |
| `shared/scripts/sap_activation_log.vbs` | **SE11 / DDIC GUI-scripting VBS only — do NOT include in SE38/SE37/SE24/SE91 (no equivalent menu in those transactions)** | Activation-log capture. Include via `ExecuteGlobal FSO.OpenTextFile("%%ACTIVATION_LOG_VBS%%",1).ReadAll()`. Functions: `CaptureActivationLog(oSess, sObjectName, sOutDir, kEnter, kBack)` → returns "" on failure or absolute path of saved log file on success; `ExtractTopActivationError(sLogPath)` → returns the top error line from the log (empty string if none). After Activate, when `sbar.MessageType = "E"` or `"A"`, call `CaptureActivationLog` then echo `ACTIVATION_LOG: <path>` and `ACTIVATION_ERROR: <top-error>` so the operator sees the specific failure instead of the generic "refer to log" SAP popup. Walks Utilities > Activation Log → Log > Save Local File via menu indices captured from a SAP GUI recording of the SE11 activation-log walk (`Record_SE11_ActivateErrorLog_01.vbs`, S/4HANA 1909). Re-record on releases that move the menus. The `Utilities > Activation Log` menu is a DDIC-worklist concept and exists ONLY in SE11; SE38/SE37/SE24/SE91 surface activation errors inline in the source-code editor + status bar (read via `wnd[0]/sbar.Text` — already done in those skills). |
Expand Down
Loading