Skip to content

feat(data-access)!: drop startedAt/result/error from Preflight schema (SITES-47254)#1740

Merged
GeezerPelletier merged 10 commits into
mainfrom
SITES-47254-drop-preflight-lifecycle-cache-attrs
Jul 3, 2026
Merged

feat(data-access)!: drop startedAt/result/error from Preflight schema (SITES-47254)#1740
GeezerPelletier merged 10 commits into
mainfrom
SITES-47254-drop-preflight-lifecycle-cache-attrs

Conversation

@GeezerPelletier

@GeezerPelletier GeezerPelletier commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Schema-layer half of SITES-47254. Drops the startedAt, result, and error attributes from the Preflight schema — they mirror columns on the preflights table that no longer exist after the mysticat-data-service migration.

⚠️ DO NOT MERGE before spacecat-api-service#2713 is deployed. Today's spacecat-api-service PreflightDto.toDetailJSON still calls preflight.getStartedAt() / getResult() / getError(). If this PR publishes a new major before #2713 deploys and any service picks up the bump, every GET /sites/:siteId/preflights/:preflightId request will throw TypeError: preflight.getStartedAt is not a function and return 500. The api-service migration to await preflight.getAsyncJob() already merged on the branch in #2713 — that PR just needs to deploy first.

After this change, lifecycle internals live only on the joined AsyncJob:

const preflight = await dataAccess.Preflight.findById(preflightId);
const asyncJob = await preflight.getAsyncJob();
asyncJob.getStartedAt();
asyncJob.getResult();
asyncJob.getError();

Preflight.status and Preflight.endedAt remain on the schema as a small denormalized cache for hot list-view reads — the projector (mysticat-projector-service#216) keeps them in sync.

Why the bump is a major (feat!)

Removing schema attributes removes the auto-generated getStartedAt() / getResult() / getError() and corresponding setters from the Preflight model. Any downstream consumer that calls those on Preflight will throw TypeError: preflight.getResult is not a function after upgrading.

Today the only known consumer is spacecat-api-service, and PR #2713 has already migrated getPreflightById to source those fields from the joined AsyncJob. No other usages found via grep across the workspace.

Changes

  • src/models/preflight/preflight.schema.js — drop startedAt, result, error attributes. Updated header comment explaining the AsyncJob ownership.
  • src/models/preflight/index.d.ts — drop the getStartedAt / getResult / getError and corresponding setter declarations; getEndedAt return is now string | undefined (not | null) to match the runtime — normalizeModelValue maps DB NULL → JS undefined.
  • src/models/async-job/index.d.ts — add getStartedAt() / setStartedAt() / getEndedAt() / setEndedAt() declarations (runtime getters exist via the schema; the TS surface was missing them — this PR makes them load-bearing since consumers are now redirected to asyncJob.getStartedAt()). Return types use string | undefined for the same reason; header comment documents the convention.
  • test/fixtures/preflights.fixture.js — strip the dropped fields from the three fixture rows; one-line comment explains where the lifecycle data lives now.
  • test/unit/models/preflight/preflight.model.test.js — drop the three startedAt / result / error describe blocks and the corresponding fields from mockRecord.
  • test/unit/models/preflight/preflight.collection.test.js — drop stale startedAt field from mockRecord (no test was failing because the schema discards unknown fields on read, but the residue misled future readers).
  • test/unit/models/preflight/preflight.schema.test.js — drop the now-irrelevant error attribute validate-suite, and add a regression guard for SITES-47254 block asserting the three attributes are NOT declared (re-introduction fails CI here rather than only at downstream new electrodb.Service(...) time).

Verification

  • Touched-file lint clean.
  • npx mocha packages/spacecat-shared-data-access/test/unit/models/preflight/*.test.js packages/spacecat-shared-data-access/test/unit/models/async-job/*.test.js55 passing, 0 failing, including the new regression guards.
  • npx mocha --timeout 30000 packages/.../electrodb-service-construction.test.js — passes; the cross-entity Service construction guard validates the full registry still constructs cleanly after the schema change.
  • CI Test job — currently failing on an ECR image manifest issue (mysticat-data-service:v5.24.1 not found) that's unrelated to this PR. Tracking separately if it doesn't auto-resolve on the next CI run.

Deploy ordering

Step What Why
1 spacecat-api-service#2713 deploys Stops calling preflight.getResult() / getError() / getStartedAt() — migrates to asyncJob.getXxx().
2 This PR merges + publishes Major bump removes the now-unused getters from Preflight.
3 Consumers npm i @adobe/spacecat-shared-data-access@<new-major> when convenient No functional change required in api-service (already on the new path post-#2713).

The data-service column drop (mysticat-data-service#746) is order-independent relative to this PR — it can land before or after; either ordering keeps the model and DB in sync (both stop carrying the columns).

Related

🤖 Generated with Claude Code

… (SITES-47254)

These three attributes mirrored columns on the preflights table that no
longer exist after the mysticat-data-service migration in SITES-47254.
PostgREST select=* against the new table shape returns no result/error/
started_at, and the auto-generated getters/setters would either return
null shadows of the AsyncJob source of truth (read-side) or 400 PostgREST
on writes against the missing columns (write-side).

After this change:
- Preflight.startedAt / result / error are gone from the schema, the
  .d.ts surface, and the test fixtures. The auto-generated
  getStartedAt() / getResult() / getError() and corresponding setters are
  removed.
- Consumers that need scan lifecycle internals fetch the joined AsyncJob
  via `await preflight.getAsyncJob()` and read getStartedAt() /
  getResult() / getError() there.
- Preflight.status and Preflight.endedAt remain on the schema as a small
  denormalized cache for hot list-view reads; the projector keeps them
  in sync (see mysticat-projector-service#216 / SITES-47253).

A new regression guard in preflight.schema.test.js asserts the three
attributes are NOT declared on the schema, so future re-introduction
fails CI here rather than only at downstream consumer Service
construction time.

This is a breaking change for any consumer outside spacecat-api-service
that calls those getters/setters on Preflight. Today there are no such
consumers — spacecat-api-service#2713 already migrated to the AsyncJob
join. Conventional commits marker `feat!` triggers a major bump.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@clotton clotton left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clean, well-scoped removal with good regression guards and thorough test cleanup. The schema change, the three absence assertions, and the comment trail are all correct. Two issues need attention before merge, and two smaller items are worth addressing.

Findings

1. BLOCKER — AsyncJob/index.d.ts is missing getStartedAt() / setStartedAt() — this PR makes them load-bearing

The updated schema comment and the PR description both redirect consumers to await preflight.getAsyncJob() then call asyncJob.getStartedAt(). The runtime getter exists (async-job.schema.js declares startedAt), but src/models/async-job/index.d.ts has no getStartedAt() or setStartedAt() declaration. Any TypeScript consumer following the migration guidance gets a compile error on asyncJob.getStartedAt(). This gap pre-existed the PR but becomes blocking the moment it is the only advertised path.

Fix: Add to src/models/async-job/index.d.ts:

getStartedAt(): string | undefined;
setStartedAt(startedAt: string): void;

2. MEDIUM — getEndedAt() return type should be string | undefined, not string | null

postgrest.utils.js::normalizeModelValue() converts DB nullundefined and skips setting the key (if (normalized !== undefined) { acc[...] = ... }). The auto-generated getter returns this.record['endedAt'], which is undefined (key absent) when the column is NULL in Postgres — never null. The declaration getEndedAt(): string | null is therefore wrong for the PostgREST v3 implementation. Callers doing getEndedAt() === null will silently miss the branch on the common unfinished-job case. (The deleted declarations getStartedAt(): string | null etc. had the same bug — those are gone, but getEndedAt() survives with it.)

Fix: Change line 24 of index.d.ts to getEndedAt(): string | undefined;

3. MEDIUM — Deploy-ordering dependency is real and the companion PR is still open

spacecat-api-service/src/dto/preflight.js lines 29, 31-32 still call preflight.getStartedAt(), preflight.getResult(), preflight.getError(). The PR description correctly identifies this and says "safe to land any time after spacecat-api-service#2713 deploys." However, #2713 is still open. If this schema bump is published to npm and picked up by the API service before #2713 merges, every GET /preflights/:id request that reaches toDetailJSON will throw TypeError: preflight.getStartedAt is not a function and return 500. The description could be hardened: make the dependency explicit in the PR body (e.g., "DO NOT merge before #2713 is deployed") or add a peerDependencies / package-level note.

4. MINOR — preflight.collection.test.js mockRecord still carries startedAt (missed cleanup)

The PR correctly cleaned preflights.fixture.js and preflight.model.test.js, but preflight.collection.test.js line 40 still has startedAt: '2025-06-01T10:00:01.000Z' in its local mockRecord. The schema silently discards unknown fields on read so no test fails, but the fixture misleads future readers into thinking startedAt is still a Preflight attribute.

Fix: Delete startedAt: '2025-06-01T10:00:01.000Z' from the collection test's mockRecord.

getStatus(): string;
getCreatedBy(): { email: string; displayName?: string };
getStartedAt(): string | null;
getEndedAt(): string | null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return type should be string | undefined, not string | null.

postgrest.utils.js::normalizeModelValue() maps DB nullundefined and skips writing the key to this.record entirely. The auto-generated getter returns this.record['endedAt'], which is undefined (absent key) when ended_at is NULL — never null. Callers checking getEndedAt() === null will silently miss the "not yet ended" branch.

Change to:

getEndedAt(): string | undefined;


import type { AsyncJob, BaseCollection, BaseModel, Site } from '../index.js';

// SITES-47254: startedAt/result/error live on AsyncJob — fetch via getAsyncJob().

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description and this comment both direct consumers to asyncJob.getStartedAt(), but AsyncJob/index.d.ts has no getStartedAt() declaration.

The async-job.schema.js declares startedAt as an attribute (the runtime JS getter exists), but the TypeScript surface omits it. Any typed consumer following this migration guidance will get a compile error.

Please add to src/models/async-job/index.d.ts:

getStartedAt(): string | undefined;
setStartedAt(startedAt: string): void;

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @GeezerPelletier,

⚠ Degraded review - no spec document was found for this change (searched the PR links, the touched repos' docs, the architecture/guidelines docs, and linked Jira). This review covers code-level quality but could not validate the change against an agreed design, so confidence is reduced. Add a spec link (PR template section 4) and re-request review for a full-confidence pass.

Verdict: Approve - clean, well-scoped schema removal with proper regression guards and clear migration path documented.
Complexity: MEDIUM - medium diff size (schema attribute removal + corresponding test updates).
Changes: Drops startedAt/result/error attributes from the Preflight schema, centralizing lifecycle data on the joined AsyncJob entity (5 files).
Note: CI checks are currently failing - the integration test job cannot pull mysticat-data-service:v5.24.1 from ECR (image not found). This is an infrastructure issue, not a defect introduced by this PR. Unit tests and lint pass. Resolve the image availability before merge.


Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 2m 6s | Cost: $4.63 | Commit: 8c0d42afd56bd51b62132390da07f5082dea82ad
If this code review was useful, please react with 👍. Otherwise, react with 👎.

@MysticatBot MysticatBot added ai-reviewed Reviewed by AI complexity:medium AI-assessed PR complexity: MEDIUM labels Jun 26, 2026
… fix return type (review #1740)

Addresses Chris's review on PR #1740:

Blocker — `AsyncJob/index.d.ts` was missing `getStartedAt()` /
`setStartedAt()` declarations. The runtime getters exist
(`async-job.schema.js` declares the attribute), but the TS surface
omitted them — this PR's schema comment and the PR description redirect
consumers to `await preflight.getAsyncJob()` then `asyncJob.getStartedAt()`,
so the gap was pre-existing but became blocking the moment this PR
makes that the only advertised path. Added both declarations with
return type `string | undefined` (DB NULL → JS undefined via
normalizeModelValue).

Medium — `Preflight.getEndedAt()` return was still `string | null` from
the original .d.ts. `normalizeModelValue` maps DB NULL to JS `undefined`
on read (the key is skipped on `this.record` entirely), so callers
checking `=== null` would silently miss the unfinished-scan branch.
Changed to `string | undefined`. Added a header comment on
`AsyncJob/index.d.ts` documenting the convention; the Preflight type
points at it.

Minor — Removed the stale `startedAt: '2025-06-01T10:00:01.000Z'` from
the `mockRecord` in `preflight.collection.test.js`. Schema silently
discards unknown fields on read so no test was failing, but the residue
misled future readers into thinking startedAt was still a Preflight
attribute.

Verified: 55 unit tests passing across preflight + async-job suites.
Lint clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

This PR will trigger a patch release when merged.

@GeezerPelletier

Copy link
Copy Markdown
Contributor Author

Thanks @clotton — all four findings addressed in a6deaea9 + a PR-description update.

Blocker #1 — Missing getStartedAt() / setStartedAt() on AsyncJob/index.d.ts

Real gap that this PR made load-bearing. The runtime getters exist (async-job.schema.js declares the attribute), but the TS surface omitted them — typed consumers following the migration guidance (await preflight.getAsyncJob()asyncJob.getStartedAt()) would have hit a compile error. Added both:

getStartedAt(): string | undefined;
getEndedAt(): string | undefined;
setStartedAt(startedAt: string): void;
setEndedAt(endedAt: string): void;

Return types use string | undefined per your other finding — see below. Also added getEndedAt/setEndedAt for symmetry since those were silently missing from the TS surface too.

Medium #2Preflight.getEndedAt() return type string | nullstring | undefined

Same root cause as the wire-contract bug you flagged on PR #2713 — normalizeModelValue maps DB NULL → JS undefined, never null. The remaining declaration was wrong. Fixed:

getEndedAt(): string | undefined;

Added a header comment on async-job/index.d.ts documenting the convention so future hands writing nullable-column declarations get it right by default; the Preflight type points back at it. (Existing pre-PR ... | null declarations on getResult / getError / getMetadata survive untouched — they're load-bearing legacy for downstream consumers, and tightening them would be a separate breaking change.)

Medium #3 — Deploy-ordering hardening

Updated the PR description with a prominent ⚠️ block at the top:

DO NOT MERGE before spacecat-api-service#2713 is deployed.

…and a deploy-ordering table at the bottom that explicitly walks the three steps (api-service deploys → this PR merges/publishes → consumers bump). The risk is real — a downstream service picking up the new major before #2713 deploys would 500 every preflight detail request.

Minor #4 — Stale startedAt in preflight.collection.test.js mockRecord

Removed. The schema's silent-discard-of-unknown-fields meant no test was failing, but you're right that it misled future readers.

CI Test failure (unrelated)

The failing job is the integration-test pull of mysticat-data-service:v5.24.1 from ECR returning manifest unknown: Requested image not found. That tag predates my changes (this PR doesn't touch the IT image reference), so it's environmental — likely an ECR retention prune. Mentioned it in the verification block of the updated PR description; if it doesn't auto-resolve on the next CI run I'll file a separate ticket to update the image reference.

State

  • 55 unit tests passing across preflight + async-job suites.
  • Lint clean on touched files.
  • Re-requesting review.

@GeezerPelletier GeezerPelletier requested a review from clotton June 26, 2026 17:21

@clotton clotton left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All findings from the prior review resolved:

  • AsyncJob/index.d.ts missing lifecycle declarationsgetStartedAt(), getEndedAt(), setStartedAt(), setEndedAt() all added with correct string | undefined return types (not | null), matching the normalizeModelValue DB-null → undefined runtime contract. The header comment explaining why | undefined is correct is a useful guard against future === null mistakes.
  • Preflight/index.d.ts wrong return typegetEndedAt() corrected to string | undefined; getStartedAt(), getResult(), getError() removed cleanly with a redirect comment to getAsyncJob().
  • Stale startedAt in collection test — removed from the mockRecord.
  • Schema regression guard — the new 'attribute surface' describe block pins that startedAt/result/error are absent from the Preflight schema, preventing silent re-introduction.

LGTM.

@GeezerPelletier GeezerPelletier enabled auto-merge (squash) June 26, 2026 17:42
GeezerPelletier and others added 8 commits June 26, 2026 13:58
Six main-merges since the last commit on this branch shifted workspace
package.json files (dependency bumps from semantic-release cycles). Run
`npm install` to bring the lockfile back in sync so the CI 'Verify
package-lock.json is in sync' guard passes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI runs `npm install --package-lock-only --ignore-scripts` on a newer
npm than my local (11.6.2). The newer npm writes `"peer": true` markers
on @aws-sdk/* peer dependency entries; my local doesn't. Ran the same
command via `npx npm@latest` to match the CI-produced lockfile
byte-for-byte.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@GeezerPelletier GeezerPelletier disabled auto-merge July 3, 2026 10:36
@GeezerPelletier GeezerPelletier requested review from MysticatBot, clotton and rainer-friederich and removed request for MysticatBot July 3, 2026 10:42

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @GeezerPelletier,

⚠ Degraded review - no spec document was found for this change (searched the PR links, the touched repos' docs, the architecture/guidelines docs, and linked Jira). This review covers code-level quality but could not validate the change against an agreed design, so confidence is reduced. Add a spec link (PR template section 4) and re-request review for a full-confidence pass.

Verdict: Approve - clean, well-scoped schema removal with all prior review findings addressed and proper regression guards in place.
Complexity: MEDIUM - medium diff; schema attribute removal across one service boundary.
Changes: Drops startedAt/result/error attributes from the Preflight schema, adds lifecycle getter/setter TS declarations to AsyncJob, and updates tests with regression guards preventing re-introduction (8 files).
Note: CI checks are currently pending - the integration test job cannot pull mysticat-data-service:v5.24.1 from ECR (image not found). This is an infrastructure issue, not a defect introduced by this PR. Unit tests and lint pass per the author's verification.

Non-blocking (1): minor issues and suggestions
  • nit: preflight.model.test.js sets endedAt: null in mockRecord but the runtime (via normalizeModelValue) would produce undefined for a DB NULL - the getter assertion to.be.null tests an impossible runtime state. Pre-existing, not introduced by this PR.

Previously flagged, now resolved

  • AsyncJob index.d.ts now declares getStartedAt() / setStartedAt() / getEndedAt() / setEndedAt() with correct string | undefined return types.
  • Preflight getEndedAt() return type corrected from string | null to string | undefined.
  • Stale startedAt removed from preflight.collection.test.js mockRecord.
  • PR description hardened with explicit "DO NOT MERGE before spacecat-api-service#2713 is deployed" warning and deploy-ordering table.

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 2m 38s | Cost: $4.70 | Commit: 4ca0d0104bbe3f030e09ef62f5e52ac2a1975c55
If this code review was useful, please react with 👍. Otherwise, react with 👎.

@GeezerPelletier GeezerPelletier merged commit 5b728bb into main Jul 3, 2026
6 checks passed
@GeezerPelletier GeezerPelletier deleted the SITES-47254-drop-preflight-lifecycle-cache-attrs branch July 3, 2026 15:26
solaris007 pushed a commit that referenced this pull request Jul 3, 2026
## [@adobe/spacecat-shared-data-access-v4.0.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v3.81.0...@adobe/spacecat-shared-data-access-v4.0.0) (2026-07-03)

### ⚠ BREAKING CHANGES

* **data-access:** drop startedAt/result/error from Preflight schema (SITES-47254) (#1740)

### Features

* **data-access:** drop startedAt/result/error from Preflight schema (SITES-47254) ([#1740](#1740)) ([5b728bb](5b728bb)), closes [#2713](https://github.com/adobe/spacecat-shared/issues/2713) [#2713](https://github.com/adobe/spacecat-shared/issues/2713) [post-#2713](https://github.com/adobe/post-/issues/2713)
@solaris007

Copy link
Copy Markdown
Member

🎉 This PR is included in version @adobe/spacecat-shared-data-access-v4.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-reviewed Reviewed by AI complexity:medium AI-assessed PR complexity: MEDIUM released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants