Skip to content

Extend the cross-system target guard from read paths to write paths - #8

Merged
KexiHe merged 1 commit into
mainfrom
fix/cross-system-gui-target-write-paths
Aug 6, 2026
Merged

Extend the cross-system target guard from read paths to write paths#8
KexiHe merged 1 commit into
mainfrom
fix/cross-system-gui-target-write-paths

Conversation

@KexiHe

@KexiHe KexiHe commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Why

The 2026-08-06 incident (S4D/100 vs S4H/400) happened because a skill resolves its SAP target twice, through two chains that can disagree: Connect-SapRfc walks pin → GUI-active → default → sole-profile, while AttachSapSession walks hint → SAPDEV_SESSION_PATH → sole-connection → refuse. /sap-se38 downloaded S4H's Z_EXCEPTION_1 while the RFC leg read S4D's — same program name, 7 lines apart, and neither output said which system it came from. Re-uploading the "fixed" download would have reverted a year of live edits on the other system.

AssertSapGuiTarget + Set-SapGuiTargetExpectation already closed the read path. This closes the write paths, where the same silent retarget is a bad write rather than a bad read.

What changed

Two SKILL.md-side defects, both per-fenced-block — each fenced block runs as its own process, and that boundary is what breaks them.

(a) Dead session pin. Generator blocks resolved the AI-session pin into $env:SAPDEV_SESSION_PATH, but cscript was launched from a later, separate block. The export died with the generator, the attach lib found no env var, and Strategy 3 (sole-connection) silently discarded the pin. Fixed by resolving in the generator and baking into %%SESSION_PATH%% (Strategy 1) — a Const in the emitted VBS, so it survives the process boundary.

(b) Missing target expectation. Set-SapGuiTargetExpectation only works in the process that launches cscript. Without it AssertSapGuiTarget has nothing to enforce, and a deploy aimed at the wrong system writes there instead of refusing.

40 skills migrated, 102 blocks (58 dead pins + 44 unverified write blocks), starting with the six flagship deploy skills: se38, se37, se11, se24, se91, se21.

CI gate

Gates 13 and 14 of the parallel-safe attach contract, in scripts/check-consistency.mjs. Shipped as WARN ratchets over the 102 offenders with a hard error on the six flagship skills, then promoted to unconditional hard errors once both counts reached zero — the same path the bare-cscript and screen-baseline gates took.

Two gate corrections that the migration surfaced (both are real semantics, not workarounds):

  • Tier-3-exempt VBS are skipped by gate 14. sap_check_gui_login_status.vbs and sap_gui_security_warmup.vbs never include %%ATTACH_LIB_VBS%%, so AssertSapGuiTarget never runs and the expectation is inert. Cleared 6 false positives (dev-init, gui-probe ×4, file-transfer's login probe).
  • Gate 13 accepts any child process, not just cscript. The defect is "exports and then exits", not "exports without cscript". /sap-update-addon deliberately bridges the pin into a child powershell -File …detect.ps1 that resolves the session itself; children inherit the environment, so that pin is live.

Reviewer notes

  • Deliberate non-migrations, matching existing documented exclusions: sap-atc and the read-only skills are not write-capable; sap-stms is off SAFETY_GATE_SKILLS because an import's target SID differs from the pinned connection — its baked pin targets the domain-controller session, and the import target stays under its own W2/W3 PROD gate. Both are called out in the code comments.
  • Some read-only skills got the expectation anyway (se16n, st22, explain-object, where-used-list). Not required by the gate, but the incident that motivated all of this was a read.
  • Per CLAUDE.md Directive 2, every skill edit was made manually with Read/Edit — no scripted refactoring. Scripts were used only for read-only analysis and one throwaway negative test that was reverted and verified.
  • Gate promotion was verified by reverting both fixes in sap-snro: the checker failed with exactly the two expected errors, then went green on restore.
  • contributing/parallel_safe_session_attach.md records the ratchet history (102 → 0), the promotion, and both gate refinements; gotchas 4 and 5 now point at gates 13/14.

Verification

node scripts/check-consistency.mjs (what .github/workflows/validate.yml runs) exits 0 with no warnings:

OK: 4 plugins, 123 skills, all manifests aligned at version 0.8.1, Tier 3 attach contract clean, screen-baseline coverage 136/136

No live SAP run — these are SKILL.md instruction blocks plus a static checker.

🤖 Generated with Claude Code

The 2026-08-06 S4D/100 vs S4H/400 incident had two SKILL.md-side causes that
the attach-lib fix alone could not close. Both are per-fenced-block, because
each fenced block runs as its own process:

(a) Dead session pin. Generator blocks resolved the AI-session pin into
    $env:SAPDEV_SESSION_PATH, but cscript was launched from a LATER, separate
    block -- so the export died with the generator, the attach lib found no env
    var, and Strategy 3 (sole-connection) silently discarded the pin. Fixed by
    resolving in the generator and baking into %%SESSION_PATH%% (Strategy 1),
    which is a Const in the emitted VBS and survives the process boundary.

(b) Missing target expectation. Set-SapGuiTargetExpectation only works when it
    runs in the process that launches cscript. Without it AssertSapGuiTarget has
    nothing to enforce, so a deploy aimed at the wrong system writes there
    instead of refusing.

Migrated all 40 affected skills (102 blocks: 58 dead pins + 44 unverified
write blocks), starting with the six flagship deploy skills (se38, se37, se11,
se24, se91, se21).

CI gate (scripts/check-consistency.mjs), gates 13 and 14 of the parallel-safe
attach contract. Shipped as WARN ratchets over the 102 offenders with a hard
error on the six flagship skills; promoted to unconditional hard errors once
both counts reached zero, per the bare-cscript / screen-baseline precedent.

Two gate corrections found during the migration:
  * Tier-3-exempt VBS are skipped by gate 14 -- sap_check_gui_login_status.vbs
    and sap_gui_security_warmup.vbs never include %%ATTACH_LIB_VBS%%, so the
    expectation is inert there (cleared 6 false positives).
  * Gate 13 accepts any child process, not just cscript. sap-update-addon
    deliberately bridges the pin into a child powershell that resolves the
    session itself; children inherit the environment, so that pin is live.

Deliberately not given an expectation, matching existing exclusions: sap-atc
and the read-only skills are not write-capable, and sap-stms keeps its own
TARGET-based PROD gate (an import's target SID differs from the pinned
connection -- its baked pin targets the domain-controller session).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@KexiHe
KexiHe merged commit 2283c60 into main Aug 6, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant