test(analyzer): add PX1121 MissingSchemaMutationGuard test coverage#671
Open
kbibelhausen wants to merge 2 commits into
Open
test(analyzer): add PX1121 MissingSchemaMutationGuard test coverage#671kbibelhausen wants to merge 2 commits into
kbibelhausen wants to merge 2 commits into
Conversation
New Roslyn analyzer detecting unguarded schema mutations in
PXDatabase.Execute calls. Catches the bug pattern that breaks
CustomizationPlugin re-publish:
PXDatabase.Execute("ALTER TABLE SOOrder ADD UsrFoo int NULL")
// Error PX1121: this ALTER will fail on the second publish
Recognized guard patterns:
- IF NOT EXISTS (SELECT ... FROM sys.columns/tables/indexes ...)
- IF EXISTS (...)
- IF COL_LENGTH('<table>', '<column>') IS NULL
- IF OBJECT_ID('<object>', '<type>') IS NULL
- IF INDEXPROPERTY(OBJECT_ID('<table>'), '<index>', 'IndexID') IS NULL
Implementation:
- Per-mutation guard detection (action-keyword tracking)
- Comment + string literal stripping before regex match
- Constant string concatenation via SemanticModel.GetConstantValue
- Interpolated string analysis with hole placeholders
- Severity: Warning, Category: Acuminator
Documented limitations (heuristic-based, not full T-SQL parse):
- Dynamic SQL silently skipped
- Interpolation holes standing in for DDL keywords not modeled
- SELECT-as-then before mutation = rare false negative
- ELSE-branch mutations after a then-branch action = false positive
Codifies Studio B's CLAUDE.md Rule Acumatica#24 (Acumatica <Sql> scripts and
PXDatabase.Execute do NOT re-execute idempotently across publishes
unless guarded explicitly).
Codex review gate: PASS (3 iterations).
Files:
- src/Acuminator/Acuminator.Analyzers/StaticAnalysis/MissingSchemaMutationGuard/MissingSchemaMutationGuardAnalyzer.cs (310 LOC)
- docs/diagnostics/PX1121.md (159 LOC)
- Resource + descriptor wiring across 5 support files
- 7 positive cases: BareAlterTableAdd, MultipleUnguardedMutations, UnrelatedGuardDoesNotSatisfyMutation, ConstantStringConcatenation, InterpolatedIdentifiers, BareCreateTable, BareCreateIndex - 7 negative cases: ColLengthGuard, IfNotExistsSysColumns, ObjectIdGuardCreateTable, IndexPropertyGuardCreateIndex, BeginEndBlockUnderGuard, NonSchemaMutationPlugin, AlterInCommentIsIgnored All source files use `using PX.Data;` only (no CustomizationPlugin reference) so they compile cleanly in the test harness. Build verification deferred to upstream Windows CI (macOS lacks net48). Codex review: CLEAR TO SHIP (no actionable issues found).
c6faf6a to
c0ad119
Compare
5 tasks
kbibelhausen
added a commit
to studio-b-ai/acuops-website
that referenced
this pull request
May 20, 2026
Adds a horizontal data ribbon between section 03 Ticker and section 05
How it works, per the canonical site-update spec at
~/Documents/brain/library/2026-05-17-acuops-site-update-spec.md §
'NEW SECTION — Open Source data strip'.
3 cells, Day-1 qualitative state per Pass 2 (Option A):
acumatica-lint · just shipped · v0.1.0 on pypi
→ https://pypi.org/project/acumatica-lint/
acuminator/pxsb · 1 pr open at acumatica · 2 queued
→ Acumatica/Acuminator#671 (PX1121)
field · 25 validators · 60+ failure modes encoded
→ https://github.com/studio-b-ai/acumatica-lint#what-it-catches
Mission Control register (Pass 5): --surface bg, --signal-dim border,
JetBrains Mono labels, --text color, tabular-nums, hard corners,
hover shifts to --signal (terminal green).
Anti-patterns avoided per spec:
- No 3-column card grid (Mission Control AI slop blacklist #1)
- No icons in colored circles
- No emoji as status indicators
- No border-left colored accents on cells
Section eyebrows renumbered 01/08 → 01/09 across all 8 existing
sections (anchor IDs preserved per Pass 7 lock — #pricing, #ticker
etc still work). New section gets id='open-source'.
Mobile (<768px) stacks vertically with 48px touch targets per Pass 6
a11y locks; prefers-reduced-motion disables hover transition.
Co-authored-by: Kevin Bibelhausen <kevin@bibelhausen.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds test coverage for the PX1121 MissingSchemaMutationGuard analyzer introduced alongside the production code. No production code was modified.
Positive cases (diagnostic expected at line 9, col 4)
Diagnostic_BareAlterTableAddALTER TABLE … ADDwith no guardDiagnostic_MultipleUnguardedMutationsALTER TABLE … ADDstatementsDiagnostic_UnrelatedGuardDoesNotSatisfyMutationIF EXISTS … PRINTguard that does not cover theALTER TABLEDiagnostic_ConstantStringConcatenation"ALTER TABLE SOOrder " + "ADD …"Diagnostic_InterpolatedIdentifiers$"ALTER TABLE {tableName} ADD {columnName} …"(line 11, col 4)Diagnostic_BareCreateTableCREATE TABLEwith noOBJECT_ID/IF NOT EXISTSguardDiagnostic_BareCreateIndexCREATE INDEXwith noINDEXPROPERTYguardNegative cases (no diagnostic expected)
NoDiagnostic_ColLengthGuardIF COL_LENGTH(…) IS NULLbeforeALTER TABLE … ADDNoDiagnostic_IfNotExistsSysColumnsIF NOT EXISTS (SELECT 1 FROM sys.columns …)NoDiagnostic_ObjectIdGuardCreateTableIF OBJECT_ID(…, 'U') IS NULLbeforeCREATE TABLENoDiagnostic_IndexPropertyGuardCreateIndexIF INDEXPROPERTY(OBJECT_ID(…), …, 'IndexID') IS NULLbeforeCREATE INDEXNoDiagnostic_BeginEndBlockUnderGuardIF COL_LENGTH … BEGIN … ENDblockNoDiagnostic_NonSchemaMutationPluginINSERTonly — not a schema mutationNoDiagnostic_AlterInCommentIsIgnoredALTERappears only inside--line comment,/* */block comment, and a string literalNotes
using PX.Data;only; noPX.Web.Customizationreference required because the analyzer fires on anyPXDatabase.Executecall regardless of enclosing class.ExternalDependency.csprojtargetsnet48, unavailable on macOS).