Skip to content

fix(project-engine-client): retype aio-create-prompt-v2's 200 response (CR14)#1765

Merged
rainer-friederich merged 6 commits into
mainfrom
fix/aio-create-prompt-response-shape
Jul 2, 2026
Merged

fix(project-engine-client): retype aio-create-prompt-v2's 200 response (CR14)#1765
rainer-friederich merged 6 commits into
mainfrom
fix/aio-create-prompt-response-shape

Conversation

@rainer-friederich

Copy link
Copy Markdown
Contributor

1. Abstract

Corrects the vendored Semrush swagger's response type for POST /v2/.../aio/prompts (id-based prompt create) via the spec overlay, and switches the mock's handler for that route from a raw response bypass to Counterfact's normal validated response path.

2. Reasoning

While building PR #1764 (the id-based prompt-write mock routes), a live probe against adobe-hackathon.semrush.com confirmed POST /aio/prompts actually returns a paginated list wrapper — {page, total, items: [{id, name}], existing_count} — but the vendored public swagger types its 200 response as a bare model.StringIDName ({id, name}), a spec/live drift. That PR shipped a raw {status, body, contentType} bypass to return the correct live shape, since going through Counterfact's normal validated path would have failed against the wrong schema. This PR closes that gap properly via the package's existing spec-correction mechanism instead of leaving the bypass as the permanent shape.

3. High-level overview of the changes

  • Adds correction CR14 to spec/overlays/corrections.yaml: retypes aio-create-prompt-v2's 200 response from model.StringIDName to the already-vendored model.StringIDNameListResponse wrapper (extended with an existing_count field it was missing), following the same remove-then-add pattern already used by CR7/CR11 for other endpoints whose response type needed replacing rather than merging.
  • Regenerates src/generated/types.ts from the corrected spec.
  • Updates the mock's POST /v2/.../aio/prompts handler to return its 200 and 500 responses through $.response[...].json(...) (Counterfact-validated) instead of the raw bypass, now that the schema is correct; the 405 quota response stays a raw bypass since 405 isn't a declared response for this operation (same pattern as every other quota-gated route in this package).

4. Required information

6. Additional information outside the code

The corrected response shape was verified against the same 2026-07-02 live production probe session (against adobe-hackathon.semrush.com) that PR #1764 was built from — a single-item POST /aio/prompts batch returned {page, total, items, existing_count}, not a bare {id, name}.

7. Test plan

(a) Ran this package's unit suite (includes the overlay freshness gate, which asserts every correction — including the new CR14 actions — still applies and is still necessary against the vendored spec) and its e2e suite, which boots the mock and exercises the corrected route over real HTTP through the now-validated response path; also ran lint and test:types.
(b) No environment-specific verification applies — this package isn't deployed as a service; consumers pick up the corrected type on their next @adobe/spacecat-shared-project-engine-client version bump.

🤖 Generated with Claude Code

…e (CR14)

The vendored public swagger types POST /v2/.../aio/prompts's 200 response
as a bare model.StringIDName ({id, name}); live actually returns a
paginated list wrapper {page, total, items: [{id, name}], existing_count}
(verified live 2026-07-02, same probe session as PR #1764). Reuses the
already-vendored model.StringIDNameListResponse wrapper (extended with
existing_count) via the overlay mechanism instead of a new schema, so the
mock's POST handler for this route now goes through Counterfact's normal
$.response[200].json(...) validation rather than the raw bypass PR #1764
needed while the spec was still wrong.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@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 @rainer-friederich,

Verdict: Approve - clean, well-scoped spec correction following established overlay patterns.
Complexity: LOW - small diff, single package.
Changes: Retypes aio-create-prompt-v2's 200 response via spec overlay CR14 and migrates the mock handler from raw bypass to Counterfact's validated response path (3 files).

Non-blocking (2): minor issues and suggestions
  • nit: Stray blank # comment line between CR14 description and the "Freshness gate" section in corrections.yaml - the other CR blocks butt directly against the next section without a separator. Consider removing for consistency. - packages/spacecat-shared-project-engine-client/spec/overlays/corrections.yaml:78
  • suggestion: CR14a extends StringIDNameListResponse (shared with two deprecated endpoints) with existing_count, which only the aio-create-prompt-v2 operation returns. Consider adding a brief note in the CR14a description that the field is endpoint-specific, so future readers don't assume it's universal. - packages/spacecat-shared-project-engine-client/spec/overlays/corrections.yaml:365

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

@MysticatBot MysticatBot added ai-reviewed Reviewed by AI complexity:low AI-assessed PR complexity: LOW labels Jul 2, 2026
…nt-specific

Addresses MysticatBot's non-blocking suggestion on PR #1765: the
StringIDNameListResponse schema is also used by two unrelated deprecated
endpoints that don't return existing_count — note that explicitly so a
future reader doesn't assume it's a universal field on that shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@rainer-friederich

Copy link
Copy Markdown
Contributor Author

Addressed MysticatBot's non-blocking review (4617167888) in `e5c55952`.

  • suggestion: CR14a's existing_count addition to StringIDNameListResponse should note it's endpoint-specific (two other, unrelated deprecated endpoints share that schema and don't return the field) — done, expanded the description.
  • nit: "stray blank # line before the Freshness gate section, inconsistent with other CR blocks" — declined, this is inaccurate. That blank line predates CR14 entirely: it's the same separator that's always sat between the CR list and the "Freshness gate" paragraph (confirmed via the pre-CR14 diff — CR13 already butted against it the same way). CR14 didn't introduce or change that boundary.

No CI-relevant changes (description-text only; regenerated types.ts is byte-identical).

… (CR14 follow-up)

Live re-verification (serenity-docs#24 §3.1 gate 2, run 2026-07-02) shows
POST /v2/.../aio/prompts responds 201 Created, not 200 — CR14 retyped the
response body shape but left the status code wrong. The sibling name-based
create endpoint (aio/prompts/tagged) already correctly documents/returns
201 in this same swagger, so the v2 (id-based) operation's vendored 200
looks like an upstream copy/paste inconsistency.

Retargets CR14b/c from '200' to '201', regenerates types.ts, updates the
mock handler and e2e assertion, and corrects docs/mock-usage.md. Also
documents (without implementing) a separate known gap: live writes this
endpoint to draft only, but the mock has no prompt-level draft/publish
state at all — flagged for the spec owner rather than fixed unilaterally.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@rainer-friederich

Copy link
Copy Markdown
Contributor Author

Live-API validation

Re-verified this PR's claims against the same live probe session (serenity-docs#24 §3.1 gate 2, run 2026-07-02) plus a fresh read-only GET against the same funded test project — the tag baseline matched exactly, confirming the probe transcript is current.

Body shape: confirmed correct — {page, total, items:[{id,name}], existing_count} matches gate 2 verbatim.

Status code — was wrong, fixed here: the live probe records POST aio/prompts → **201**, not 200. CR14 retyped the response body schema but left the status code untouched at 200 (inherited from the vendored swagger, which only documents a 200 for this operation). The sibling name-based create endpoint (aio/prompts/tagged) already correctly documents/returns 201 in this same swagger — the v2 (id-based) operation's 200 looks like an upstream copy/paste inconsistency Semrush never fixed.

Pushed a follow-up commit that:

  • Retargets CR14b/c to remove the stale 200 and add 201 instead (same remove-then-add pattern)
  • Regenerates types.ts
  • Updates the mock handler ($.response[201].json(...)) and its docstring
  • Updates the e2e assertion (expect(res.status).to.equal(201))
  • Fixes the status code in docs/mock-usage.md

Unit suite (incl. overlay freshness gate), e2e suite, test:types, and lint all pass locally; CI is green on the new commit.

Separate, NOT fixed here — flagged for a decision: live writes this endpoint to draft only (by_tags without ?draft= is empty until the existing publish step runs — gate 2 + spec §4 deliverable-1 item 2, which explicitly asks this mock to gate on "the mock's existing publish semantics"). But no such prompt-level draft/publish state exists anywhere in this mock — publish.js only flips a project-level publish_status that no prompt read consults, and by_tags.js has no draft filtering at all. This predates this PR (from #1764) and is restated here rather than introduced by it. It's a bigger design fork (would need prompt-level draft state across by_tags.js/publish.js, not just this route) so I've documented it in the handler's docstring rather than implementing it unilaterally — needs a decision from whoever owns serenity-docs#24.

🤖 Generated with Claude Code

…nt_id (CR15)

Counterfact rejected a literal `parent_id: null` on PATCH aio/tags/{tag_id}
because the vendored TreeNodeRequest schema typed it as a non-nullable
string, surfacing as a 502 to callers sending the live-verified
promote-to-root request (serenity-docs#24 gate 1). The mock handler also
always overwrote parent_id even when the caller omitted the key, silently
un-parenting a rename-only PATCH.

Introduced by: N/A

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Both prompt-create endpoints (aio/prompts/tagged, id-based aio/prompts)
already stamp is_new: true, but the mock had no consumer of that field —
by_tags always showed a freshly created prompt immediately, contradicting
live (serenity-docs#24 §3.1 gate 2 + gate 6, verified 2026-07-02: a
just-created prompt is invisible via by_tags without ?draft=true until
the project's publish endpoint runs).

by_tags.js now filters out is_new:true prompts by default (the already
swagger-declared `draft` query param opts back in); publish.js flips
every draft prompt in the project to is_new:false on a successful
publish, mirroring the real consumer's existing create -> publishAffected
-> publishProject sequence.

Scoped to prompts only — tags have an analogous live draft/publish split
this mock does not model, called out in mock-usage.md as a separate,
larger-blast-radius gap rather than folded into this change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@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 @rainer-friederich,

Verdict: Approve - well-scoped spec corrections with matching mock, type, doc, and test updates.
Complexity: HIGH - medium diff; mock routes match API surface signal.
Changes: Retypes aio-create-prompt-v2's response (CR14, 200->201 + list wrapper), adds CR15 (nullable parent_id for TreeNodeRequest), and implements the 3-way parent_id switch in the PATCH handler (6 files).

Previously flagged, now resolved

  • CR14a existing_count description expanded to note it is endpoint-specific (addressed in e5c5595)
Non-blocking (3): minor issues and suggestions
  • nit: Stray backtick line-wrap artifact in {tag_id}.js header comment - the backtick before the newline at "...request validation. `" pairs with the one after "children_count", making the inline code span wrap across the JSDoc * prefix. Re-wrap so `children_count` starts on a fresh line. - packages/spacecat-shared-project-engine-client/mock/counterfact/routes/v2/workspaces/{id}/projects/{project_id}/aio/tags/{tag_id}.js:27
  • suggestion: The two new PATCH tests rename and re-parent childTagId (Ridge, then Trail-at-root) without restoring or documenting the mutation. A brief trailing comment like "NB: leaves childTagId renamed to Trail at root" would save the next person adding a test from a 30-minute debug session. - packages/spacecat-shared-project-engine-client/test/e2e/project-engine-mock.e2e.js:933
  • suggestion: The 500 unknown-tag error path in prompts.js now goes through $.response[500].json(...) (Counterfact-validated) instead of the old raw bypass. Consider adding a quick e2e assertion (POST /aio/prompts with a bogus tag_id -> 500) to exercise the validated path and confirm the factory shape matches the overlay's declared 500 schema. - packages/spacecat-shared-project-engine-client/mock/counterfact/routes/v2/workspaces/{id}/projects/{project_id}/aio/prompts.js:62

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

@rainer-friederich

Copy link
Copy Markdown
Contributor Author

Follow-up on the earlier status-code fix: also closed the draft/publish-visibility gap flagged in that review.

Problem: the mock had no draft/live distinction for prompts — a freshly-created prompt was immediately visible via by_tags, but live keeps it in a draft tree until the project's publish endpoint runs (verified 2026-07-02, serenity-docs#24 §3.1 gate 2 + gate 6). Both prompt-create endpoints already stamped is_new: true on creation; nothing was reading it.

Fix (prompts only):

  • aio/prompts/by_tags.js — default (non-draft) reads now exclude is_new: true prompts; ?draft=true (already a declared swagger query param, no overlay change needed) sees everything, matching live's draft tree.
  • v1/.../publish.js — a successful publish now also flips every still-draft prompt in that project to is_new: false, moving it into the default view — mirroring the real consumer's create → publish sequence.
  • Added two new e2e lifecycle tests (create → invisible by default → visible with draft:true → publish → visible by default) for both the tagged and id-based create paths; updated the existing immediate-read-after-create tests to pass draft:true since they're testing CRUD mechanics, not the gating.

Explicitly out of scope: tags (GET /aio/tags) have an analogous live draft/publish split that this mock does NOT model. Flagged in docs/mock-usage.md and in publish.js's docstring as a separate, unaddressed gap — folding it in here would have required rewriting the tag-focused e2e suite, which wasn't part of this change.

100% coverage maintained; all CI checks green.

…tems on #1765

Fix a stray backtick that split an inline code span across the JSDoc
line wrap, and document that the two new PATCH e2e tests mutate the
seed's childTagId in place for the rest of their own test only.

Introduced by: N/A

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rainer-friederich

Copy link
Copy Markdown
Contributor Author

Addressed both non-blocking suggestions in 0c0e8f1c.

MysticatBot:

  • nit: stray backtick splitting the children_count inline code span in {tag_id}.js's header — fixed.
  • suggestion: document that the new PATCH tests mutate childTagId in place — added an NB comment to both tests noting beforeEach resets state so it doesn't leak between tests.
  • suggestion: add an e2e assertion for POST /aio/prompts 500ing on a bogus tag_id — declined, this already exists: test/e2e/project-engine-mock.e2e.js:577 ("500s atomically on an unknown tag id") asserts exactly res.status === 500 against the same validated response path.

@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 @rainer-friederich,

Verdict: Approve - well-scoped spec corrections with matching mock, type, doc, and test updates.
Complexity: HIGH - large diff (673 lines); mock routes match API surface signal.
Changes: Retypes aio-create-prompt-v2's response (CR14, 200->201 + list wrapper), adds CR15 (nullable parent_id for TreeNodeRequest), implements draft/publish gating and the 3-way parent_id switch in the PATCH handler (9 files).

Previously flagged, now resolved

  • Stray backtick splitting children_count inline code span in {tag_id}.js header (fixed in e5c5595)
  • PATCH tests now document state mutation via NB comments (added in 0c0e8f1)
  • CR14a existing_count description expanded to note it is endpoint-specific (addressed in e5c5595)
Non-blocking (1): minor issues and suggestions
  • nit: The comment at corrections.yaml:78 (blank # line between the CR list and the "Freshness gate" paragraph) predates this PR and is consistent with the file's existing formatting - confirmed no action needed per author's earlier note.

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

@MysticatBot MysticatBot added complexity:high High complexity PR and removed complexity:low AI-assessed PR complexity: LOW labels Jul 2, 2026
@rainer-friederich rainer-friederich merged commit 33315e1 into main Jul 2, 2026
8 checks passed
@rainer-friederich rainer-friederich deleted the fix/aio-create-prompt-response-shape branch July 2, 2026 13:34
solaris007 pushed a commit that referenced this pull request Jul 2, 2026
## [@adobe/spacecat-shared-project-engine-client-v1.7.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-project-engine-client-v1.7.0...@adobe/spacecat-shared-project-engine-client-v1.7.1) (2026-07-02)

### Bug Fixes

* **project-engine-client:** retype aio-create-prompt-v2's 200 response (CR14) ([#1765](#1765)) ([33315e1](33315e1)), closes [#1764](#1764) [#1764](#1764)
@solaris007

Copy link
Copy Markdown
Member

🎉 This PR is included in version @adobe/spacecat-shared-project-engine-client-v1.7.1 🎉

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:high High complexity PR released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants