Skip to content

fix: 14 bug-scan findings across persistence, engine, and API routes - #55

Merged
projectamazonph merged 13 commits into
mainfrom
claude/claude-md-documentation-9v1uby
Aug 3, 2026
Merged

fix: 14 bug-scan findings across persistence, engine, and API routes#55
projectamazonph merged 13 commits into
mainfrom
claude/claude-md-documentation-9v1uby

Conversation

@projectamazonph

Copy link
Copy Markdown
Owner

Summary

13 atomic fixes for issues surfaced by a code-review pass over the engine, store, and API layers. Each commit is independently tested and scoped to one bug.

  • Persistence: partialize only persisted state.state, silently dropping every feature slice (profiles, drills, trainer, missions, reports, bulk, integrity) on reload — trainee progress vanished on every refresh. Now persists everything non-function, so future slices don't repeat the bug.
  • duplicateCampaign: hardcoded all targets/negatives/ads to the first new ad group regardless of origin, silently losing data on multi-ad-group campaigns.
  • Campaign API: PUT/DELETE verified ownership via a separate query but mutated by bare id — now scoped by userId on the mutation itself (defense-in-depth against future refactors).
  • deleteProfile: could leave activeProfileId pointing at nothing after deleting the only profile.
  • setTargetBid/adjustTargetBid: silently accepted NaN/negative bids instead of failing fast, unlike sibling engine functions.
  • pauseTarget: pushed a blank history entry for an unknown target id instead of no-op'ing.
  • updateCampaignSettings: no validation on dailyBudget/defaultBid, and the store slice typed updates as Record<string, unknown>, discarding compile-time safety.
  • addNote/renameProfile: threw uncaught ValidationError from inside a Zustand set() updater on blank input, crashing the caller.
  • startMission: didn't validate the mission id exists, unlike the analogous startDrill.
  • generateReport: silently produced 0 rows for the valid searchTerm/placement report types.
  • getNegativeCandidates: its default minOrders: 0 made the ACOS check unreachable for any term with sales.
  • API JSON reads: unguarded JSON.parse on stored campaign columns meant one corrupted row 500'd the entire list.
  • Registration: accepted 1-character passwords and any string as an email.
  • simulateDays: no validation on days, allowing negative values to corrupt cumulative metrics (not reachable via current UI, but a latent API gap).

Two related findings were deliberately not fixed here (called out in the corresponding commit messages): missions' lack of a real action-correctness check (would require wiring action-tracking into every mission-relevant UI event, a feature-sized change) and registration's email-enumeration behavior (a UX-contract change, lower severity for an offline training simulator).

Test plan

  • npm run type-check — clean
  • npm test — 624/624 passing (new tests added for every fix)
  • npm run build — succeeds

Generated by Claude Code

claude added 13 commits August 3, 2026 07:23
partialize only whitelisted `state.state`, so every field a feature
slice adds at the store's top level (profiles, drills, trainer notes,
certification checklist, missions, reports, bulk, integrity) was
silently dropped from localStorage on every reload — a trainee's
progress vanished the moment they refreshed the page.

Persist every non-function field instead of a hardcoded key list, so
the next feature slice added to the store doesn't quietly repeat this
bug.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
Every target, negative, productAd, and ad was hardcoded to the first
new ad group's id (newAgId) regardless of which ad group it
originally belonged to. Duplicating a campaign with multiple ad
groups silently merged everything into one, leaving the other ad
groups empty.

Build a map from old ad group id to new ad group id and use it to
re-attach each item to its own ad group instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
Deleting the only remaining profile reset activeProfileId to the
literal 'p-default' without reinserting a default profile into the
roster, leaving profiles: [] and activeProfileId pointing at nothing
— any selector doing profiles.find(p => p.id === activeProfileId)
would come back undefined.

Reseed defaultProfile() when the roster would otherwise go empty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
PUT and DELETE checked ownership via a separate findFirst({ id,
userId }) query, but the actual update/delete calls used where: { id
} alone. Not exploitable today since the check and mutation run in
the same request, but any future refactor that separates them (a
queued job, a transfer feature, reordered code) could silently
reintroduce cross-user access with no compiler or test signal.

Prisma's extended where-unique-input lets id and userId be combined
directly on the mutation, so the mutation itself now proves
ownership instead of relying entirely on the preceding check.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
- setTargetBid (and adjustTargetBid, which calls it) never validated
  newBid, unlike sibling addTarget — a NaN or negative bid was
  silently clamped via Math.max instead of throwing ValidationError,
  contradicting the codebase's fail-fast convention. Added the same
  assertFiniteNonNegative guard addTarget already uses.

- pauseTarget didn't check whether targetId matched an existing
  target before proceeding, unlike removeTarget/adjustTargetBid/
  setTargetStatus — an unknown id pushed a blank string into history
  and returned a new object reference for what should have been a
  no-op. Now early-returns the campaign unchanged, matching its
  sibling functions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
updateCampaignSettings never validated dailyBudget/defaultBid (unlike
normalizeCampaign, which clamps them), so a negative budget or a NaN
bid was written straight into the campaign and even rendered into
history text like "$-50.00". The store slice compounded this by
typing `updates` as Record<string, unknown>, discarding the compile-
time protection the function's own signature already provided.

Added assertFiniteNonNegative checks matching the rest of the
engine's fail-fast convention, and tightened the slice's type to the
function's actual Partial<Pick<Campaign, ...>> signature.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
…ndary

Both actions called an engine function that throws ValidationError on
blank input (assertNonEmpty) from inside a Zustand set() updater,
where the exception propagates uncaught — a blank note or profile
rename would crash the calling event handler instead of failing
gracefully.

The engine functions are correct to throw (fail-fast is the intended
contract there); the gap was the store not guarding the boundary
before calling them, unlike the equivalent UI-level checks elsewhere
in the app. Added the same non-empty guard at the store action level
so blank input is a no-op regardless of caller.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
Unlike the analogous startDrill (which falls back to createSession()
for an unknown id), startMission built a session for any id with no
existence check. Since completeMissionStep looks the mission back up
by id on every call, an unresolvable id left the session permanently
stuck at step 0 with no error — indistinguishable from a UI freeze.

Mirrors startDrill's pattern: fall back to an empty/idle session
instead of a session that can never resolve.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
Only 'campaign'/'adGroup'/'target' were implemented; the two other
valid, user-selectable ReportTypes silently returned rows: [] with a
status of 'completed' and no error — requesting a searchTerm or
placement report produced an apparently successful but empty export.

ReportRow has no per-type schema, so the existing simulated-row
generator now covers every type in REPORT_TYPES instead of a
hardcoded subset.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
The exclusion condition was `st.orders >= minOrders && st.sales > 0`
with a default minOrders of 0. Since orders can never be negative,
`st.orders >= 0` is always true, so the condition collapsed to
`st.sales > 0` — any search term with even $0.01 of sales was
excluded before its ACOS was ever checked, hiding genuinely
catastrophic-ACOS terms from negation.

Mirrors the sibling getHarvestCandidates function: a single
`st.orders >= minOrders` gate with minOrders defaulting to 1 (already
converted at least once → protected from blanket negation;
everything else proceeds to the ACOS check). Also adds the test
coverage this function had none of.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
GET /api/campaigns, GET /api/campaigns/[id], and GET /api/sync all
called JSON.parse directly on 10 stored JSON columns with no
try/catch, while the sync write path is careful to validate input. A
single row with a corrupted or truncated JSON value (from a prior
bug, a direct DB edit, or column truncation) threw an uncaught
SyntaxError, 500ing the entire list rather than just that row.

Added a shared safeJsonParse helper that falls back to the same
empty defaults already used for null columns, so one bad row no
longer takes down every other campaign in the response.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
Registration accepted any non-empty password (a single character was
enough) and any string as an "email" with no format check. Added a
basic email-format check and an 8-character minimum password length.

Left the user-enumeration behavior (differing response for an
existing email) unchanged for now — fixing that properly means
changing the registration UX to not confirm success/failure by
status code, which is a larger behavioral change than this pass, and
lower real-world severity for an offline training simulator with no
real user data at stake.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
…trics

No validation existed on days — a negative value flowed unclamped
into spend (Math.min picked the more-negative term), decreasing a
campaign's cumulative spend/impressions/clicks below their prior
values with a nonsensical history entry. Not reachable through the
current UI (every call site uses the days=7 default), but it's a
public engine function with no guard, unlike the rest of the engine's
fail-fast convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Jpq3N94psa27k8xASDy8B
Copilot AI review requested due to automatic review settings August 3, 2026 07:48
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
amazon-ad-console Ready Ready Preview Aug 3, 2026 7:48am

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@projectamazonph, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b817321e-ca31-4e58-a6d5-721fd25fdc65

📥 Commits

Reviewing files that changed from the base of the PR and between f95f40c and 5316dde.

📒 Files selected for processing (26)
  • src/app/api/auth/register/__tests__/route.test.ts
  • src/app/api/auth/register/route.ts
  • src/app/api/campaigns/[id]/__tests__/route.test.ts
  • src/app/api/campaigns/[id]/route.ts
  • src/app/api/campaigns/route.ts
  • src/app/api/sync/__tests__/route.test.ts
  • src/app/api/sync/route.ts
  • src/engine/ad-console/__tests__/feature-stores.test.ts
  • src/engine/ad-console/__tests__/persistence.test.ts
  • src/engine/ad-console/core/__tests__/campaignGoal.test.ts
  • src/engine/ad-console/core/__tests__/engine.test.ts
  • src/engine/ad-console/core/__tests__/simulation.test.ts
  • src/engine/ad-console/core/engine/campaign.ts
  • src/engine/ad-console/core/engine/negative.ts
  • src/engine/ad-console/core/engine/target.ts
  • src/engine/ad-console/core/simulation.ts
  • src/engine/ad-console/core/slices/core.ts
  • src/engine/ad-console/features/missions/__tests__/engine.test.ts
  • src/engine/ad-console/features/missions/engine.ts
  • src/engine/ad-console/features/profiles/store.ts
  • src/engine/ad-console/features/reports/__tests__/engine.test.ts
  • src/engine/ad-console/features/reports/engine.ts
  • src/engine/ad-console/features/trainer/store.ts
  • src/engine/ad-console/store.ts
  • src/lib/__tests__/json.test.ts
  • src/lib/json.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ecc-tools

ecc-tools Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Analyzing 200 commits...

@ecc-tools

ecc-tools Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Analysis Complete

Generated ECC bundle from 13 commits | Confidence: 65%

View Pull Request #56

Repository Profile
Attribute Value
Language TypeScript
Framework Not detected
Commit Convention conventional
Test Directory mixed
Changed Files (26)
Metric Value
Files changed 26
Additions 542
Deletions 60

Top hotspots

Path Status +/-
src/app/api/campaigns/[id]/__tests__/route.test.ts added +88 / -0
src/engine/ad-console/core/__tests__/campaignGoal.test.ts modified +76 / -1
src/app/api/auth/register/__tests__/route.test.ts added +64 / -0
src/app/api/sync/__tests__/route.test.ts modified +62 / -0
src/engine/ad-console/core/__tests__/engine.test.ts modified +38 / -0

Top directories

Directory Files Total changes
src/engine/ad-console/core/__tests__ 3 125
src/app/api/campaigns/[id]/__tests__ 1 88
src/app/api/auth/register/__tests__ 1 64
src/app/api/sync/__tests__ 1 62
src/engine/ad-console/__tests__ 2 46
Analysis Depth Readiness (commit-history, 14%)

ECC Tools uses this to decide whether recommendations should stay at commit-history/setup guidance or expand into CI, security, harness, reference-set, AI-routing, and team backlog work.

Area Status Evidence / Next Step
Commit history Ready 13 commits sampled
CI/CD signals Missing Add workflow files or CI troubleshooting evidence so ECC Tools can reason about pipeline setup.
Security evidence Missing Add AgentShield, audit, SARIF, SBOM, or security review evidence so recommendations can cover security posture.
Harness configuration Missing Add Claude, Codex, OpenCode, Zed, dmux, MCP, plugin, or cross-harness config evidence for harness-agnostic recommendations.
Reference/eval evidence Missing Add fixtures, golden traces, reference sets, or evaluator benchmarks so deeper recommendations have regression evidence.
AI routing and cost controls Missing Add model-routing, budget, usage, or cost-control files before relying on AI-heavy automation recommendations.
Team handoff and project tracking Missing Add roadmap, runbook, project, Linear, or follow-up tracking docs so generated work can land in a team queue.
Reference Set Readiness (0/7, 0%)
Area Status Evidence / Next Step
Deep analyzer corpus Missing Add analyzer fixture, golden, benchmark, or reference-set files that can catch analyzer regressions.
RAG/evaluator comparison Missing Add retrieval or evaluator reference-set comparison fixtures with expected ranking behavior.
PR salvage/review corpus Missing Add stale-PR, review-thread, reopen-flow, or salvage reference cases for queue cleanup automation.
Discussion triage corpus Missing Add public discussion triage fixtures, golden cases, or reference sets for informational, answered, and no-response classifications.
Harness compatibility Missing Add cross-harness, adapter-compliance, or harness-audit evidence for Claude, Codex, OpenCode, Zed, dmux, and agent surfaces.
Security evidence Missing Attach security evidence such as SBOMs, SARIF, audit reports, or AgentShield evidence packs.
CI failure-mode evidence Missing Add captured CI failure logs, dry-run fixtures, or troubleshooting docs for common workflow failure modes.
Likely Future Issues (4)
Severity Signal Why it may show up
HIGH API contract changes may ship without integration coverage 7 API surface paths changed; 0 integration or e2e tests changed
MEDIUM API implementation changes may ship without contract artifact updates 7 API implementation paths changed; 0 API contract/spec files changed
HIGH Auth or permission changes may ship without security regression coverage 2 auth/permission paths changed; 0 auth-focused integration or e2e tests changed
HIGH Security-sensitive changes may ship without scanner evidence 2 security-sensitive paths changed; 0 security scanner or security-focused validation artifacts changed
  • API contract changes may ship without integration coverage: The PR changes API or route-facing files but does not touch any obvious integration or end-to-end tests.
  • API implementation changes may ship without contract artifact updates: The PR changes API implementation files but does not touch any obvious OpenAPI, GraphQL, or contract/spec artifact.
  • Auth or permission changes may ship without security regression coverage: The PR changes auth, session, middleware, or permission-sensitive files without touching any obvious auth-focused integration or end-to-end tests.
  • Security-sensitive changes may ship without scanner evidence: The PR touches billing, secrets, auth, webhooks, agent, or CI-sensitive surfaces without adding obvious security scanner, code scanning, or security-focused validation evidence.
Suggested Follow-up Work (4)
Type Suggested title Targets
PR test: add integration coverage for src/app/api/auth/register/__tests__/route.test.ts + src/app/api/auth/register/route.ts src/app/api/auth/register/__tests__/route.test.ts, src/app/api/auth/register/route.ts
PR docs: sync API contract for src/app/api/auth/register/__tests__/route.test.ts + src/app/api/auth/register/route.ts src/app/api/auth/register/__tests__/route.test.ts, src/app/api/auth/register/route.ts
PR test: add auth coverage for src/app/api/auth/register/__tests__/route.test.ts + src/app/api/auth/register/route.ts src/app/api/auth/register/__tests__/route.test.ts, src/app/api/auth/register/route.ts
PR security: add scanner evidence for src/app/api/auth/register/__tests__/route.test.ts + src/app/api/auth/register/route.ts src/app/api/auth/register/__tests__/route.test.ts, src/app/api/auth/register/route.ts
  • test: add integration coverage for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts: Backfill integration or end-to-end coverage for the changed API surface before more contract changes land.
  • docs: sync API contract for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts: Backfill the missing API contract or spec update before another implementation change lands on top of the same surface.
  • test: add auth coverage for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts: Backfill auth or permission regression coverage before another access-control change lands on the touched surface.
  • security: add scanner evidence for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts: Backfill explicit scanner or code-scanning evidence before another security-sensitive change lands on the touched surface.

Copy-ready bodies

test: add integration coverage for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts

## Summary
- Add integration or end-to-end coverage for the recently changed API surface.

## Why
- Backfill integration or end-to-end coverage for the changed API surface before more contract changes land.

## Touched paths
- `src/app/api/auth/register/__tests__/route.test.ts`
- `src/app/api/auth/register/route.ts`

## Validation
- Add or extend integration / e2e coverage for the changed API, route, or contract surface.
- Exercise the touched endpoints or route handlers against realistic request / response flows.

docs: sync API contract for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts

## Summary
- Update the API contract artifact that should reflect the recently changed implementation surface.

## Why
- Backfill the missing API contract or spec update before another implementation change lands on top of the same surface.

## Touched paths
- `src/app/api/auth/register/__tests__/route.test.ts`
- `src/app/api/auth/register/route.ts`

## Validation
- Update the relevant OpenAPI, GraphQL, or contract/spec artifact used by this repo.
- Run the contract validation, docs generation, or API verification flow that depends on that artifact.

test: add auth coverage for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts

## Summary
- Add auth, session, or permission regression coverage for the recently changed security-sensitive surface.

## Why
- Backfill auth or permission regression coverage before another access-control change lands on the touched surface.

## Touched paths
- `src/app/api/auth/register/__tests__/route.test.ts`
- `src/app/api/auth/register/route.ts`

## Validation
- Add or extend integration / e2e coverage for the changed auth, session, middleware, or permission surface.
- Exercise allowed and denied flows, invalid or expired credentials, or equivalent access-control boundary cases.

security: add scanner evidence for src/app/api/auth/register/tests/route.test.ts + src/app/api/auth/register/route.ts

## Summary
- Add security scanner or code-scanning evidence for the recently changed security-sensitive surface.

## Why
- Backfill explicit scanner or code-scanning evidence before another security-sensitive change lands on the touched surface.

## Touched paths
- `src/app/api/auth/register/__tests__/route.test.ts`
- `src/app/api/auth/register/route.ts`

## Validation
- Run or add the relevant security scanner, code scanning, secret scanning, or dependency/security review check for the touched surface.
- Attach the scanner output, SARIF/code-scanning result, or focused security regression test to the follow-up PR.
- Confirm the changed auth, billing, webhook, secret-handling, agent, or CI surface has an explicit pass/fail gate.
Detected Workflows (2)
Workflow Description
engine-bugfix-with-test Fixes a bug in a core engine or feature logic file and adds or updates a corresponding test to cover the fixed behavior.
api-route-bugfix-with-test Fixes a bug in an API route handler and adds or updates a test to verify the fix.
Generated Instincts (25)
Domain Count
git 4
code-style 8
architecture 1
testing 8
workflow 4

After merging, import with:

/instinct-import .claude/homunculus/instincts/inherited/Amazon-ad-console-instincts.yaml

Files

  • .claude/ecc-tools.json
  • .claude/skills/Amazon-ad-console/SKILL.md
  • .agents/skills/Amazon-ad-console/SKILL.md
  • .agents/skills/Amazon-ad-console/agents/openai.yaml
  • .claude/identity.json
  • .codex/config.toml
  • .codex/AGENTS.md
  • .codex/agents/explorer.toml
  • .codex/agents/reviewer.toml
  • .codex/agents/docs-researcher.toml
  • .claude/homunculus/instincts/inherited/Amazon-ad-console-instincts.yaml
  • .claude/commands/engine-bugfix-with-test.md
  • .claude/commands/api-route-bugfix-with-test.md

ECC Tools | Everything Claude Code

Copilot AI 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.

Pull request overview

This PR addresses a set of bug-scan findings across the ad-console persistence layer, core engine logic, and API routes to prevent data loss/corruption, enforce fail-fast validation, and harden multi-user access boundaries.

Changes:

  • Fix Zustand persistence to avoid silently dropping feature-slice state on reload by persisting all non-function fields (with regression tests).
  • Correct/validate core engine behaviors (campaign duplication across ad groups, bid/days/budget validation, report generation coverage, mission/profile edge cases) with targeted unit tests.
  • Harden API routes by safely parsing stored JSON columns and scoping campaign mutations by userId, plus add basic registration input validation with tests.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/lib/json.ts Adds safeJsonParse helper to prevent corrupted JSON from crashing API responses.
src/lib/tests/json.test.ts Unit tests for safeJsonParse behavior (valid/invalid/empty inputs).
src/engine/ad-console/store.ts Fixes persistence partialize to include all non-function state (prevents feature slice loss).
src/engine/ad-console/features/trainer/store.ts Guards blank note submissions to prevent uncaught validation errors in Zustand updater.
src/engine/ad-console/features/reports/engine.ts Ensures all report types generate rows (including searchTerm/placement).
src/engine/ad-console/features/reports/tests/engine.test.ts Adds regression tests for searchTerm and placement report generation.
src/engine/ad-console/features/profiles/store.ts Prevents blank rename crash; reseeds default profile when deleting the last profile.
src/engine/ad-console/features/missions/engine.ts Validates mission id existence by falling back to an empty session for unknown ids.
src/engine/ad-console/features/missions/tests/engine.test.ts Adds regression test for unknown mission id fallback behavior.
src/engine/ad-console/core/slices/core.ts Tightens updateCampaignSettings typing to preserve compile-time safety for allowed fields.
src/engine/ad-console/core/simulation.ts Adds fail-fast validation for days to prevent negative/NaN simulation inputs.
src/engine/ad-console/core/engine/target.ts Validates bid inputs; makes pauseTarget a no-op for unknown ids (prevents blank history).
src/engine/ad-console/core/engine/negative.ts Fixes negative-candidate filtering defaults/logic so ACOS checks are reachable.
src/engine/ad-console/core/engine/campaign.ts Fixes duplicateCampaign to preserve per-ad-group relationships; validates campaign setting updates.
src/engine/ad-console/core/tests/simulation.test.ts Regression tests for invalid days values.
src/engine/ad-console/core/tests/engine.test.ts Regression tests for multi-ad-group duplication, bid validation, and pauseTarget unknown-id behavior.
src/engine/ad-console/core/tests/campaignGoal.test.ts Adds tests for negative/harvest candidate selection and campaign settings validation.
src/engine/ad-console/tests/persistence.test.ts Regression tests ensuring partialize includes feature slice state and excludes functions.
src/engine/ad-console/tests/feature-stores.test.ts Regression tests for profile reseeding and no-op behaviors on blank inputs.
src/app/api/sync/route.ts Uses safeJsonParse to avoid 500s when a single row has corrupted JSON fields.
src/app/api/sync/tests/route.test.ts Test ensuring corrupted JSON in one row doesn’t break the entire sync list response.
src/app/api/campaigns/route.ts Uses safeJsonParse when listing campaigns to prevent list-wide failures on corrupted JSON.
src/app/api/campaigns/[id]/route.ts Uses safeJsonParse for single campaign fetch; scopes PUT/DELETE mutations by userId.
src/app/api/campaigns/[id]/tests/route.test.ts Tests mutation scoping for PUT/DELETE to defend against cross-user updates by id.
src/app/api/auth/register/route.ts Adds basic email format and password length validation to registration.
src/app/api/auth/register/tests/route.test.ts Tests register-route input validation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/app/api/sync/route.ts
Comment on lines 152 to +156
bidStrategy: c.bidStrategy,
placements: c.placements ? JSON.parse(c.placements) : { top: 0, product: 0, rest: 0 },
products: c.products ? JSON.parse(c.products) : [],
creative: c.creative ? JSON.parse(c.creative) : null,
metrics: c.metrics ? JSON.parse(c.metrics) : { impressions: 0, clicks: 0, spend: 0, sales: 0, orders: 0 },
adGroups: c.adGroups ? JSON.parse(c.adGroups) : [],
targets: c.targets ? JSON.parse(c.targets) : [],
searchTerms: c.searchTerms ? JSON.parse(c.searchTerms) : [],
negatives: c.negatives ? JSON.parse(c.negatives) : [],
budgetRules: c.budgetRules ? JSON.parse(c.budgetRules) : [],
history: c.history ? JSON.parse(c.history) : [],
placements: safeJsonParse(c.placements, { top: 0, product: 0, rest: 0 }),
products: safeJsonParse(c.products, []),
creative: safeJsonParse(c.creative, null),
metrics: safeJsonParse(c.metrics, { impressions: 0, clicks: 0, spend: 0, sales: 0, orders: 0 }),
Comment on lines +16 to +24
if (typeof email !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
return NextResponse.json(
{ error: 'Enter a valid email address' },
{ status: 400 }
);
}

if (typeof password !== 'string' || password.length < 8) {
return NextResponse.json(
Comment on lines +21 to +25
placements: safeJsonParse(c.placements, null),
products: safeJsonParse(c.products, []),
creative: safeJsonParse(c.creative, null),
metrics: safeJsonParse(c.metrics, null),
adGroups: safeJsonParse(c.adGroups, []),
Comment on lines +31 to +35
placements: safeJsonParse(campaign.placements, null),
products: safeJsonParse(campaign.products, []),
creative: safeJsonParse(campaign.creative, null),
metrics: safeJsonParse(campaign.metrics, null),
adGroups: safeJsonParse(campaign.adGroups, []),
@projectamazonph
projectamazonph merged commit a53e2c6 into main Aug 3, 2026
5 checks 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.

3 participants