| name | app-modernization-inception |
| description | INCEPTION phase (assessment) of the optional application-modernization module: scan an application and classify every site impacted by a database migration — embedded SQL, datasource/ORM configuration, entity mappings, stored-routine call sites, error-code handling and result-set typing. Engine-agnostic; reads the active pair's rules from engines/<pair>/app/. Invoked by app-modernization-orchestrator, which turns this assessment into the gated change plan; read-only, edits nothing. Produces migrations/<project>/05-application/01-assessment/inventory.md.
|
Application Modernization — Inception (assessment)
Find everything the migration affects, classify it, and be honest about what cannot be resolved
statically. This skill reads and reports only — it never edits.
Inputs: 00-intake/app-intake.md (stack, scope), 01-assessment/app-contract.md
(what the migration changed), engines/<pair>/app/app-config.yaml and app-sql-rules.md.
Scope discipline
Search only the in-scope source directories. Always exclude build output and dependencies:
target/, build/, bin/, obj/, dist/, out/, node_modules/, .git/, vendor/,
__pycache__/, *.class, *.jar, *.dll, minified assets.
A hit inside build output means the artifact is stale, not that there is a site to convert.
Report such files separately as stale build artifacts, and note any .bak/.orig leftovers
from previous ad-hoc conversions — they are not conversion targets.
Categories to inventory
A. Connectivity & framework configuration
Datasource URL, driver class, ORM dialect, connection pool, schema/search-path selection.
Typical locations: application.properties|yml, appsettings*.json, web.config,
persistence.xml, hibernate.cfg.xml, *.env, Helm/Terraform values, Dockerfiles, CI config.
Record the file, the key, and the current value — redact passwords (password=***).
Also scan CODE that parses or builds connection URLs — grep for jdbc:, the driver class
name, and URL-string surgery (split, substring, regex) on a datasource URL. In two real runs
this was missed by config-only scanning: display/diagnostic classes split the URL on Oracle's
@// token, and their fallback echoed the raw URL — a credential-leak path once query parameters
carry secrets. URL format assumptions are code sites, not config sites.
B. Embedded SQL
Classify each site by tier, because tier determines whether it can be converted safely:
| Tier | What | Conversion approach |
|---|
| 1 | Externalised SQL (*.sql, MyBatis/iBatis mappers, .xml) | convert directly |
| 2 | Complete statement in a constant, annotation or literal (@Query, const string) | convert directly |
| 3 | Assembled at runtime (StringBuilder, conditional fragments, dynamic WHERE) | reconstruct the full statement first, then convert; never rewrite a fragment in isolation |
| 4 | Generated by a builder/ORM criteria API, or fetched from the DB/config | usually no SQL text to edit — fix the generator, the dialect, or flag for redesign |
For each site record: file, line range, tier, whether the statement is complete, the constructs
found, and mechanical vs behavioural.
Search terms come from the pair's app-sql-rules.md. For an Oracle source, at minimum:
SYSDATE, SYSTIMESTAMP, NVL, NVL2, DECODE, DUAL, ROWNUM, CONNECT BY, (+),
MINUS, LISTAGG, TO_DATE, TO_CHAR, TRUNC, ADD_MONTHS, MONTHS_BETWEEN, NEXTVAL,
CONTAINS(, CATSEARCH, SCORE(, /*+, FETCH FIRST, REGEXP_. For SQL Server:
GETDATE, ISNULL, TOP , IDENTITY, @@IDENTITY, SCOPE_IDENTITY, LEN(, MERGE,
CROSS APPLY, OUTER APPLY, NOLOCK, + string concat, DATEADD, DATEDIFF, CONVERT(.
C. ORM entity mappings
Per stack, the same class of site:
- JPA/Hibernate:
@Table/@Column/@Entity names — especially quoted identifiers,
which pin the old case; @SequenceGenerator/@GeneratedValue strategies, type converters,
@Lob, @Enumerated, ddl-auto/schema-generation settings.
- EF Core:
ToTable/HasColumnName/DbSet naming conventions, OnModelCreating
configuration, value converters, HasDefaultValueSql (dialect-specific SQL inside the model!),
owned types, and any Database.EnsureCreated/migration invocation at startup.
- MyBatis/Dapper/SQLAlchemy/Sequelize: mapping files or attribute conventions equivalent
to the above.
Cross-check mapped names against the SOURCE catalog. For each explicit table mapping
(JPA @Table(name=…), EF ToTable("…")/DbSet conventions), confirm the named table actually
exists in the source schema. Real apps drift: one mapped Order → ToTable("Orders") while the
source table was Order — the migration surfaces such drift as a hard failure, so classify it at
inventory time (conform to the migrated name + flag), not during build fixing.
For EF Core apps specifically, record that the model emits quoted PascalCase identifiers:
a pure-LINQ app with zero SQL text still breaks against a case-folded schema, so the change plan
must include the OnModelCreating lower-case fold (see the pair's app-sql-rules).
D. Stored-routine call sites
CallableStatement, { call … }, @Procedure, EntityManager.createStoredProcedureQuery,
Dapper CommandType.StoredProcedure, EXEC. For each: the routine name as called, whether the
migration renamed it (package flattening), whether it became a function rather than a procedure,
OUT/ref-cursor parameters, and whether the routine lost an internal COMMIT (making the caller
responsible for the transaction).
E. Error handling
getErrorCode(), ORA- string matching, SQLException subclasses, @SQLExceptionTranslator,
.NET OracleException.Number, SQL Server SqlException.Number. Each must move to the target's
error identity (SQLSTATE for PostgreSQL, error numbers for MySQL) using the mapping recorded in
the migration's conversion log.
F. Result-set typing & column access
Same class of site per stack:
- JDBC:
rs.getInt("…") on a COUNT(*) (now bigint → getLong), column lookups by
the source engine's case, getObject casts, getDate vs getTimestamp.
- ADO.NET/Dapper:
reader["Col"] / reader.GetInt32(...) on counts (→ GetInt64),
ordinal-vs-name access, DataTable typing, Dapper materialization of changed types
(bit→boolean binds true/false, not 0/1).
- All stacks: boolean-vs-numeric flag reads and date/time precision reads.
G. Schema-qualified references in app code
Hard-coded SCHEMA.TABLE prefixes, and any search_path/default-schema assumption.
H. Build dependencies
The source database driver (and any Oracle/SQL-Server-only library) in pom.xml,
*.csproj, requirements.txt, package.json. Note whether the target driver is already
present — a half-finished earlier attempt is common.
Output — 01-assessment/inventory.md
- Summary table: category → site count → files touched → mechanical / behavioural / blocked.
- Per-category detail, each site with file, line, tier, current code and the construct found.
- Behavioural list — sites that can change results silently, called out separately.
- Blocked / undecidable — Tier 3/4 statements that cannot be reconstructed statically,
routines whose target shape is unknown, dynamic SQL from config or the database.
- Not-a-target — stale build artifacts,
.bak leftovers, vendored code.
- Coverage caveat, stated plainly: static scanning cannot find SQL assembled from
configuration, generated at runtime, or stored in the database. If the app has integration
tests or a query log, say so — running them is the reliable way to surface the remainder.
Do not propose fixes here; that is the change plan's job. Inventory establishes what exists.