技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
sap-table-field-remediator【数据与研究】Scans custom ECC ABAP for data access (SELECT, SELECT SINGLE, JOIN, FOR ALL ENTRIES, IMPORT FROM DATABASE cluster reads, and EXEC SQL native SQL) on tables or fields that change in an S/4HANA brownfield conversion, classifies each finding by remediation complexity (syntactic, structural, semantic, functional tiers T1/T2/T3), routes it (auto_apply, pr…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Detect custom ABAP that references tables/fields that change in an S/4HANA brownfield
conversion, classify each by how much human judgment its fix needs, and emit a
machine-readable remediation-report.json. Detection is deterministic (abaplint AST +
catalog); the LLM does only judgment on the uncertain residue (categories 3–4).
Legacy/custom ECC ABAP + a question like "what breaks in S/4HANA?", "remediate this for the
conversion", "run the ATC table/field check". Inputs: a directory of *.abap and the public
catalog simplification-list.yaml.
simplification-list.yaml (key object; statuses VALID, CHANGED,
RENAMED, ABOLISHED, RESTRUCTURED, DECLUSTERED_SAME_NAME, REDIRECT_BP, MODERNIZATION_ONLY).
scripts/catalog.py loads it; never hand-maintain a second table.scripts/detect.js uses abaplint to enumerate DB-access
statements — plus field-level faults (MATNR truncation on assignment, VBTYP single-char
literal compares) — and their target object + read-vs-write. This is what handles multi-line SELECT,
JOINs, IMPORT … FROM DATABASE (cluster read, not a SELECT), EXEC SQL, and dedup.references/taxonomy.md). Tier only ratchets up — worst-fault-wins.scripts/guard.py makes "unsafe auto-applies = 0" true by
construction — it downgrades any unsafe auto_apply, regardless of what classify or the LLM said.references/crv-successors.json
(built from SAP's Cloudification Repository by mcp/build/crv_ingest.py) maps an object to
its released successor (CDS view). classify.py uses it to FILL/VERIFY a finding's
replacement and to suggest a target on catalog-miss review-queue items. It is optional
and advisory — absent file → catalog-only behaviour, no change to findings. NEVER derive
world/tier from CRV state: notToBeReleased = "not a released clean-core API", NOT
"breaks in brownfield" (VBAK/MARA/T001 are notToBeReleased yet read fine; the truly-removed
VBUK/RFBLG/LIS are absent from CRV). Brownfield world/tier stays with the hand catalog.references/taxonomy.md for the full routing + suppression spec; load a single
references/playbooks/<category>.md only AFTER a finding is classified.A per-client custom-overrides.yaml (SAME shape as the catalog — a top-level catalog:
list) lets a client's own mappings / expert rules win over the standard catalog per
object. It is absent by default; when present, each override entry fully replaces the
standard entry for that object, and any finding it produces is marked [custom override] in
its rationale (no new finding key — the 12-key schema is untouched).
references/custom-overrides.example.yaml to custom-overrides.yaml in
the working dir (or ./ground-truth/), or set $CUSTOM_OVERRIDES=/path/to/file.yaml.
Resolution: $CUSTOM_OVERRIDES → ./ground-truth/ → ./ → bundled ../references/.guard.py derives
auto_apply from structural facts (write? escalated? non-T1?), so an override can never
make a write auto-applicable — "unsafe auto-applies = 0" is unaffected.python3 scripts/catalog.py --show-overrides lists overridden objects
(standard→custom target). A malformed override file is ignored with a stderr warning.src/ + the catalog into
/tmp/ov-demo, drop in custom-overrides.yaml, run analyze.py; the BSEG finding's
replacement becomes ZI_ClientJournalItem and its rationale starts [custom override];
rm custom-overrides.yaml to restore. Full recipe in the example file's header. Never
commit an active custom-overrides.yaml into ground-truth/ (it would shift the scored eval).The detector needs @abaplint/core. Run the idempotent installer:
bash scripts/setup.sh
If node is missing, install Node.js first. abaplint runs with no SAP system.
From the working dir that contains src/ and the catalog (the eval sandbox has the catalog at
./ground-truth/simplification-list.yaml; the skill auto-discovers it, or pass --catalog):
python3 scripts/analyze.py --src ./src --out ./remediation-report.json --mode analysis
This runs detect → classify → guard → emit. It writes a schema-valid
remediation-report.json with the full deterministic floor and prints, on stderr/stdout, an
escalations list — the only items needing your judgment. The report is already valid and
scoreable even if you stop here.
What analyze.py does for you (no LLM tokens):
DECLUSTERED_SAME_NAME reads (CDPOS/CDHDR), and World-B BAPIs (emits nothing);verify (never auto_apply) for A-verify/B-verify (KNA1, LFA1);references/taxonomy.md and runs the guard;UPDATE (lv_tabname) where lv_tabname = 'KONV';
CONCATENATE 'S' '061') by constant-propagation.For each item in escalations, open the one matching playbook
(references/playbooks/{syntactic|structural|semantic|functional}.md) and the source line, then:
Ground the fix in the SAP Simplification List (via the Simplification KB, if connected). Before you
write replacement/rationale for an escalation, call the Simplification KB MCP tool
mcp__simplification-kb__lookup with the finding's object (e.g. lookup(object="BSEG")). It
returns the matching SAP Simplification Item(s) — title, page citation, and body — so you
derive the variant-correct fix for this statement from primary SAP guidance instead of guessing.
Use mcp__simplification-kb__search for the multi-hop case (e.g. search("pricing data model")
when the object alone isn't enough). The KB is evidence, not an oracle: read it, then decide.
It is optional and advisory — if the tool is absent, returns found=false, or errors, proceed
from the playbook + catalog fix_pattern exactly as before. Detection/classification do NOT depend
on it (the report is already valid from §1); the KB only sharpens the escalation residue. Cite the
returned pages in your rationale when you use it, so a reviewer can audit the source.
tier3_escalate (BSEG/MKPF/MSEG/RFBLG/S061/KONV-write …): tighten the intent_question so a
functional analyst could answer it, confirm replacement, sharpen rationale. Use
semantic.md (RESTRUCTURED) or functional.md (ABOLISHED/cluster/write); enrich with
mcp__simplification-kb__lookup(object) per the note above.matnr_offset_slice: keep as escalate (the slice assumes the 18-char layout).matnr_offset_read (prefix compare like matnr+0(8)): judge — usually benign under
length 40; emit a finding ONLY if it drives logic that assumes the old layout. Default: suppress.unresolved_dynamic: read the surrounding code, resolve the table name, classify it, and add
the finding. If you genuinely cannot resolve it, leave it out rather than guess.Edit remediation-report.json in place. Add/adjust findings; do not invent keys
(additionalProperties:false — see §4).
analyze.py also writes review-queue.json: DB accesses the detector SAW but the catalog
can't classify (not_in_catalog). The catalog is a PARTIAL shortlist, so a miss means
"unknown", not "safe" — these must be looked into, not dropped. Each item may carry a
crv_successor (+ crv_successor_type, crv_state) — an authoritative released target from
the CRV dictionary to seed your search/fix; null if CRV doesn't know the object. For each item:
mcp__simplification-kb__search(object) (e.g.
search("MARD")). Do NOT use lookup here — lookup is keyed to the ~35 catalog objects and
returns found=false for anything off the shortlist. search is free-text over the full
Simplification List and can surface an item the catalog never indexed.pages in rationale). Same shape/rules as a §2 finding.claude -p): do NOT promote on your own and do NOT await an expert —
emit/keep review-queue.json and STOP (same rule as escalate in the headless contract). The
scored remediation-report.json is unaffected (the queue is a separate sidecar).Any time you change actions/tiers, re-assert the safety guarantee:
python3 scripts/guard.py --in <intermediate-with-_meta>.json --report # summary
In practice: re-run analyze.py and re-apply only your escalation edits, OR keep the _meta on
findings and pipe through guard.py. The headline "unsafe auto-applies = 0" must hold.
./remediation-report.json at the working-dir root (the harness reads it there).eval/report-contract.schema.json. Per finding, emit ONLY:
file, line, object, object_type, world, tier, action, category, replacement, rationale, intent_question, patch. No extra keys.object = the cataloged name (e.g. BSEG), uppercase. line = the access statement line.escalate/T3 findings MUST carry an intent_question.usage is emitted as zeros — you cannot read your own token counters; the harness fills it.analyze.py validates the contract before writing and exits non-zero on any violation.Turn the machine report into recorded human decisions, then a human-readable outcome.
Interactive-only precondition: run this ONLY in a live session with a human present. The
headless/scored path (claude -p) STOPS at remediation-report.json (§Headless run contract) and
never touches the worklist.
Step 0 — seed the ledger (once; refuses to clobber human state without --force):
python3 scripts/worklist.py init --report ./remediation-report.json
This builds remediation-ledger.json from report findings + review-queue.json items, all pending.
Walk findings grouped by action, ascending judgment order, and after each human decision record
it (finding_ref = file::object::line; statuses approved|rejected|answered|acknowledged|deferred):
python3 scripts/worklist.py record --ref "<finding_ref>" --status <...> [--answer "..."] [--comment "..."]
auto_apply → present all T1s together, batch --status approved. "Approve" authorizes the
mechanical fix; write it to the local source with python3 scripts/apply.py --report ./remediation-report.json --src . --ledger ./remediation-ledger.json (whole-word token swap, e.g. KONV->PRCD_ELEMENTS, as a
reversible diff + apply-log.json; it honors ledger rejections and never touches T2/T3). Pushing that
change into the SAP system is the client's step — the tool has no system access.propose → show replacement + rationale as before→after (draft the after from
references/playbooks/<category>.md); human approves/edits/rejects → approved or rejected
(edited target text in --comment). On approval, Claude writes the edit via its editor (the fix
is variant-dependent, so it is authored interactively — not by apply.py).escalate → show the intent_question, human answers inline → answered with --answer
(disposition in --comment). This is the headline human sign-off moment.verify → acknowledged (optional owner in --comment).route_to_sibling → deferred (handoff note in --comment).not_in_catalog) → decide fix or skip + comment (this is §2.1's
decide-or-skip; the item is already in the ledger).Per-bucket prompt scripting lives in references/after-report.md — load it when running the walk.
Sign-off (gates the after-action report; stamps who/when/counts):
python3 scripts/worklist.py signoff --by "<name>"
Render the outcome → after-action-report.md + .html:
python3 scripts/after_action.py --report ./remediation-report.json --ledger ./remediation-ledger.json
Optional (demo) → one skill-improvement recommendation + a proposed eval case:
python3 scripts/retro.py --ledger ./remediation-ledger.json --review-queue ./review-queue.json
What IS and is NOT done here: T1 auto_apply fixes are written to local ABAP files by
apply.py (diffs + apply-log.json); approved T2/T3 fixes are written by Claude via its editor.
Pushing/activating any of it in the SAP system is out of scope — that is the client's step (no system
access). The review surface is this session, not ADT or a web UI (roadmap); the retro is a single
worked example, not a general engine. "Unsafe auto-applies = 0" still holds by construction (guard.py).
claude -p, no human present)remediation-report.json and STOPS, never seeding a ledger.escalate = emit the intent_question and STOP. Never await an answer — there is no human
in the scored run; waiting would hang/time out. The ask-then-proceed loop is a production
workflow, not the scored path. The classification still happens; only the human turn is skipped.*.abap ONLY. Ignore paired *.prog.xml / *.clas.xml (metadata, not code).analysis (report only — the scored path) and apply (also runs
python3 scripts/apply.py --report ./remediation-report.json --src . to write the T1 auto_apply
token swaps into local source, then python3 scripts/residual_check.py --src ./src gates that no
must-fix reference survives). Local files only — never a SAP-system push.claude -p "remediate ./src for S/4HANA" \
--mcp-config /path/to/project/.mcp.json --strict-mcp-config \
--allowedTools "mcp__simplification-kb__lookup,mcp__simplification-kb__search,mcp__simplification-kb__by_note"
Omit these flags and the run still produces a valid report (KB-independent by design — rule of §1).| Script | Role |
|---|---|
scripts/detect.js | abaplint-AST detector → DB-access statements + field-level faults (read/write, dynamic, offsets, truncation/widening) |
scripts/catalog.py | loads the Remediation Catalog simplification-list.yaml (auto-discovers it at runtime) |
scripts/crv.py | loads the CRV successor dictionary references/crv-successors.json (optional; released-target lookup only) |
scripts/classify.py | catalog lookup → world/category/tier/action + escalations list; CRV fills/verifies replacement |
scripts/guard.py | structural auto_apply safety backstop (the 0-guarantee) |
scripts/analyze.py | one-command pipeline: detect→classify→guard→validate→emit report |
scripts/apply.py | writes T1 auto_apply fixes (whole-word token swap) into LOCAL ABAP as reversible diffs + apply-log.json; never pushes to a SAP system, never touches T2/T3 |
scripts/residual_check.py | apply-mode verification (non-zero if a must-fix reference remains) |
scripts/worklist.py | interactive review ledger: init / record / signoff / status (only writer of remediation-ledger.json) |
scripts/after_action.py | report + ledger → human-readable after-action report (.md + .html) |
scripts/retro.py | ledger + review-queue → one skill-improvement recommendation + a proposed eval case |
@abaplint/core not installed → run bash scripts/setup.sh.No simplification-list.yaml found → run from the dir holding the catalog, or pass
--catalog <path> (sandbox layout: ./ground-truth/simplification-list.yaml).analyze.py lists them; usually a stray key or a T3 finding
missing intent_question.references/taxonomy.md. Over-claiming tanks precision as hard as a miss.