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
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ jobs:
--health-retries 10
env:
ASKR_ORM_TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/askr_orm_test
ASKR_ORM_TEST_SHADOW_URL: postgresql://postgres:postgres@127.0.0.1:5432/askr_orm_test_shadow
steps:
- name: Checkout repository
uses: actions/checkout@v7
Expand All @@ -87,5 +88,11 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Create isolated scratch database
run: node tests/prepare-postgres.mjs

- name: Run PostgreSQL integration tests
run: npm run test:integration

- name: Qualify installed PostgreSQL generation with minimum peers
run: npm run test:packed
18 changes: 15 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,20 @@
- Transaction-owned migration plan/apply/resolve handles now reject use after
their transaction ends, before any SQL can run on a released connection.

- Complete PostgreSQL scratch reset with an actual database separation check,
session-owned workflow lock, normalized catalog introspection and protocol-only
query description. Unequal URL strings alone no longer authorize reset.
- Normalize the desired PostgreSQL schema inside rollback-only scratch DDL,
preserving native type/default/expression semantics without comparing codecs
or application property names to database columns.
- Quote enum type/schema names, emit one composite primary key, and defer initial
foreign keys until their referenced tables exist.
- Generate conservative nullable query results and standard PostgreSQL parser
types (int8/numeric strings, timestamp/date Date, unknown unsupported types).
- Gate real and normally installed generation/validation/no-op workflows against
isolated PostgreSQL 16, 17 and 18 scratch databases.

### Remaining release gate

- PostgreSQL shadow reset/introspection/query description are incomplete in the
advertised generation/validation workflow (#55). No ORM 0.5.0 readiness or
publication claim is made until that real workflow is qualified.
- Complete coordinated 0.5.0 packed-candidate and website qualification remains
pending. Maintainer review and approval are required before publication.
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,13 @@ askr database migration plan
askr database migration apply --yes
```

The PostgreSQL generated-artifact workflow is a 0.5.0 release blocker tracked in
[#55](https://github.com/askrjs/askr-orm/issues/55): the shipped shadow reset,
introspection and description methods are incomplete. Ordinary runtime access
and application of an already bundled migration manifest are covered separately.
PostgreSQL generated-artifact tooling uses a disposable scratch database and
proves actual separation from the target before reset. It normalizes supported
catalog shapes and describes query results without executing application queries.
See [PostgreSQL tooling](docs/postgres-tooling.md) for reset scope, supported
objects, conservative result types and connection requirements. Native and
installed PostgreSQL 16–18 CI lanes qualify the workflow. Complete coordinated
0.5.0 candidate qualification and maintainer review are still required.

Generation replays checksummed, forward-only SQL against the shadow database
before accepting it. It writes migration SQL plus one committed
Expand All @@ -122,5 +125,5 @@ cannot be interrupted.

The [API decisions](docs/0.5.0-api.md) record every retained and removed name and
its migration. The [hardening report](docs/0.5.0-hardening.md) distinguishes
regression fixes, executed characterization and the remaining release blocker.
regression fixes, executed characterization and the remaining coordinated release gates.
The package version remains 0.4.0 until the coordinated candidate is prepared.
2 changes: 1 addition & 1 deletion docs/0.5.0-api.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 0.5.0 ORM API decisions

Status: implementation candidate; API issue #53 remains open for the complete 0.5.0 packed-package and generated website review. PostgreSQL generation/validation also remains blocked by [#55](https://github.com/askrjs/askr-orm/issues/55).
Status: implementation candidate; API issue #53 remains open for the complete 0.5.0 packed-package and generated website review. The real PostgreSQL tooling fix for [#55](https://github.com/askrjs/askr-orm/issues/55) has separate native and installed workflow qualification in [PostgreSQL tooling](postgres-tooling.md).

The published 0.4.0 declarations expose 132 entrypoint/name pairs. The curated contract exposes 78: 68 root names, one CLI bridge, seven PostgreSQL names and two SQLite names. There are 54 removed named exports, plus the removed `sql.key` member. No compatibility shims are provided.

Expand Down
82 changes: 48 additions & 34 deletions docs/0.5.0-hardening.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# 0.5.0 ORM hardening evidence

Status: runtime regressions are fixed and the API candidate is qualified locally.
The package is **not release-ready**. [PostgreSQL generated-artifact tooling
#55](https://github.com/askrjs/askr-orm/issues/55) remains a release blocker, so
#53 and #54 stay open alongside the complete candidate/website qualification.
Status: the API/runtime candidate and real PostgreSQL tooling fix are qualified
locally. PostgreSQL 16–18 hosted and installed workflow checks must pass before
#55 closes. The package is **not release-ready**: #53 and #54 still require the
complete coordinated candidate/website qualification and maintainer review.

## Assertion review and real adapters

Expand Down Expand Up @@ -55,46 +55,49 @@ identifier rejection and a subsequent successful read on both databases.

The normal package gate includes strict packed TypeScript 6/7 declaration checking
without skipLibCheck, exact 78 runtime/type names, all 54 removed imports,
ColumnBuilder's type-only boundary, removed sql.key, 14 private subpaths, a normal
ColumnBuilder's type-only boundary, removed sql.key, 16 private subpaths, a normal
install of the minimum PostgreSQL peers (8.23.0/4.17.0), and root/SQLite imports
with those optional peers absent. Installed SQLite executes a complete migration
script and proves transaction rollback plus a subsequent query. The CLI bridge's
help path resolves; real PostgreSQL generation is separately blocked by #55.
help path resolves. Installed PostgreSQL generation, validation and no-op
regeneration pass with minimum peers and TypeScript 6/7 generated-artifact checks.

`npm run check` runs lint/types, V8 implementation coverage, type contracts,
packed consumer, build, the unchanged 20,000-operation/10,000-row benchmark,
publint and pack dry run. Format checking runs explicitly and in hosted CI.
The final source suite contains 82 cases: 70 ordinary cases and 12 real
The final source suite contains 100 cases: 70 ordinary cases and 30 real
PostgreSQL integration cases. A lane without the database URL skips only those
12 integration cases; it is not PostgreSQL qualification by itself.
30 integration cases; it is not PostgreSQL qualification by itself.

Coverage is measured across implementation source, including the incomplete tooling.
Coverage is measured across implementation source, including the complete private tooling path.

Measured with the real PostgreSQL URL enabled (82 cases):
Measured with both real PostgreSQL URLs enabled (100 cases):

| Implementation | Statements | Branches | Functions | Lines |
| --------------- | ------------------ | ----------------- | --------------- | ------------------ |
| total | 1289/1626 (79.27%) | 699/1023 (68.32%) | 326/399 (81.7%) | 1193/1450 (82.27%) |
| migrations.ts | 77/84 (91.66%) | 30/37 (81.08%) | 18/19 (94.73%) | 71/77 (92.2%) |
| postgres.ts | 118/141 (83.68%) | 55/76 (72.36%) | 25/30 (83.33%) | 113/124 (91.12%) |
| sqlite.ts | 122/168 (72.61%) | 40/68 (58.82%) | 31/49 (63.26%) | 112/145 (77.24%) |
| tooling-impl.ts | 280/482 (58.09%) | 155/352 (44.03%) | 51/85 (60%) | 262/432 (60.64%) |
| client.ts | 216/250 (86.4%) | 108/147 (73.46%) | 65/74 (87.83%) | 200/221 (90.49%) |
| Implementation | Statements | Branches | Functions | Lines |
| ------------------- | ------------------ | ----------------- | ---------------- | ------------------ |
| total | 1594/1887 (84.47%) | 881/1179 (74.72%) | 407/459 (88.67%) | 1474/1692 (87.11%) |
| client.ts | 216/250 (86.4%) | 106/147 (72.1%) | 65/74 (87.83%) | 200/221 (90.49%) |
| migrations.ts | 77/84 (91.66%) | 29/37 (78.37%) | 18/19 (94.73%) | 71/77 (92.2%) |
| postgres-client.ts | 21/21 (100%) | 4/4 (100%) | 7/7 (100%) | 19/19 (100%) |
| postgres-tooling.ts | 185/187 (98.93%) | 85/90 (94.44%) | 36/36 (100%) | 172/174 (98.85%) |
| postgres.ts | 94/107 (87.85%) | 55/72 (76.38%) | 19/19 (100%) | 89/92 (96.73%) |
| sqlite.ts | 122/168 (72.61%) | 40/68 (58.82%) | 31/49 (63.26%) | 112/145 (77.24%) |
| tooling-impl.ts | 400/569 (70.29%) | 249/418 (59.56%) | 93/113 (82.3%) | 374/513 (72.9%) |

Executed commands:

```sh
vp env exec --node 24.21.0 npm run fmt -- --check
ASKR_ORM_TEST_DATABASE_URL=<isolated-test-database> vp env exec --node 24.21.0 npm run check
ASKR_ORM_TEST_DATABASE_URL=<isolated-test-database> ASKR_ORM_TEST_SHADOW_URL=<disposable-scratch-database> vp env exec --node 24.21.0 npm run check
vp env exec --node 24.21.0 npm audit --json
```

The normal package CI gate has no PostgreSQL URL; its unit report is separate
from the integration proof above. The real PostgreSQL matrix executes the 12
integration cases. Coverage HTML/JSON is retained for assertion inspection.
from the integration proof above. The real PostgreSQL matrix executes 12 runtime and 18 tooling
integration cases, followed by the normal installed minimum-peer workflow. Coverage HTML/JSON is retained for assertion inspection.

Test/fixture source is excluded. In particular, incomplete tooling branches are
not treated as covered or accepted merely because runtime tests pass.
Test/fixture source is excluded. Uncovered branches remain visible in the retained coverage; test names and
source counts are not acceptance evidence.

The compatible development dependency update reduces the npm audit result from
four findings to zero. It does not change optional peer ranges or use forced
Expand All @@ -121,14 +124,25 @@ moved tooling function bodies, 40 remain token-identical; loadDatabases has this
single intentional boundary correction. This does not complete the distinct
PostgreSQL shadow workflow tracked below.

## Remaining PostgreSQL tooling blocker

An executed real PostgreSQL probe confirms that shadow reset always throws,
introspection returns database/schema identity rows rather than a normalized
schema snapshot, and description returns no columns for `SELECT $1::text AS
value`. The advertised generate/validate path calls these boundaries directly.
The prior generation unit test supplies fake implementations and therefore does
not qualify this real adapter workflow. #55 requires actual target/scratch
separation before destructive reset, complete supported-object introspection,
query description without executing application queries, and a real
`generate → validate → unchanged generate` recovery matrix for PostgreSQL 16–18.
## PostgreSQL tooling qualification

The original real PostgreSQL probe found three stubs: reset always threw,
introspection returned database/schema identity rows, and description returned
no columns. The fake-driver unit fixture did not qualify these boundaries.
The real-adapter regressions and the normal installed workflow are documented in
[PostgreSQL tooling](postgres-tooling.md). They cover actual target aliases,
unreachable target, reset rollback, deterministic supported catalogs, protocol
metadata without executing queries, concurrent description, workflow lock
ownership, real generation/byte-validation/no-op, drift/stale-artifact failure
and replay recovery. Composite keys and quoted enum names receive native tests.

The final 18-case native tooling suite fails 17 cases against the previous merged
PostgreSQL/schema/private-tooling source at
`3f595754d7e2a3135cd0ed0f5ef67b05b306e82c`; unreachable-target refusal is one
passing characterization because the old reset stub refuses everything. These
are scenarios, not 17 independent bug claims. Additional refinement RED cases
caught quoted enum SQL, missing related-table creation order, generated constraint
name differences during explicit rename, four silently ignored physical schema
attributes, duplicate query output names, and repeated parameter declarations
rejected by TypeScript. The final candidate passes all 100 tests and the installed
TypeScript 6/7 workflow.
4 changes: 4 additions & 0 deletions docs/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ interpolate application data. Placeholder rendering is dialect-owned.

Set `targetIdentity` and `scratchIdentity` in `database/index.ts`. Tooling
checks these before reset or target migration work and fails closed on a match.
The built-in PostgreSQL driver additionally proves actual target/scratch
separation before reset and owns a pinned scratch workflow session. See
[PostgreSQL tooling](postgres-tooling.md) for destructive reset scope, supported
catalog shapes, conservative query metadata and connection requirements.

Driver errors may pass through the adapter. The ORM normalizes PostgreSQL
constraint, serialization, deadlock, timeout, cancellation, and connection
Expand Down
Loading
Loading