feat(llmo-akamai): CUSTOM-default OAE onboarding (PUT deploy + caching/scope gates) - #2850
Conversation
…g/scope gates) Rework the Akamai "Optimize at Edge" (BYOCDN) onboarding for CUSTOM-default properties. Rule builder (llmo-akamai-utils.js): - Wildcard the UA match values (*GPTBot* etc.) so they match real agent strings, not only an exact "GPTBot". - Add a Caching behavior (Honor origin Cache-Control + Expires) to the OAE rule ONLY when the property's default rule has none (cfg.addCaching); cacheId is always present. Controller (llmo-akamai.js): - deploy/plan: revert JSON-Patch -> full-tree PUT (updateRuleTree, pinned ruleFormat; plan uses the client's dryRun). - Scope gate: reject a non-CUSTOM default origin (400) BEFORE creating a version, so a rejected onboarding leaves no orphan version. - Caching decision: addCaching = !defaultRuleHasCaching(tree). - Optional baseVersion (copy-from); caller email in the activation note. Tests + OpenAPI updated (119 passing, lint clean). NOTE: package.json temporarily points @adobe/spacecat-shared-akamai-client at a gist tarball for testing. Swap it back to the published version (after adobe/spacecat-shared#1825 merges + releases) before merging this PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
mergeIntoTree/buildRuleTreePatch defaulted insertIndex to 0 when the caller (the wizard) sends none, inserting "Optimize at Edge" as the FIRST child. On Akamai, sibling rules evaluate top-down and origin/caching are last-match-wins, so a later stock rule (Offload origin, Increase availability, …) clobbered the OAE origin override + cacheId and bots were never routed. Default now appends after all existing children; an explicit numeric insertIndex is still honored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
This PR will trigger a minor release when merged. |
…error Akamai's activation POST regularly exceeds the shared client's 10s request timeout on large rule trees (it re-validates the whole tree), so client.activate throws even though Akamai already queued the activation; any retry then returns 422 already-activated. Both were surfaced as a 502 "Akamai activation failed" (and a misleading "check credentials" in the UI), though the activation was in flight. The activate handler now, on error, looks up the latest activation for the exact version+network and — if it's in flight or active — reports success so the UI polls it. Genuine failures still surface. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rity) - OAE origin host now comes from env.EDGE_OPTIMIZE_EDGE_DOMAIN (falls back to live.edgeoptimize.net), so a dev/stage deployment routes AI-bot traffic to dev/stage.edgeoptimize.net instead of always prod. matchSan (*.edgeoptimize.net) covers all three, so CUSTOM cert validation still holds. - Restore the setVariable PMUSER_EDGE_OPTIMIZE_FAILOVER=TRUE on the Site Failover rule (declared FALSE, flipped on failover) for v38 parity — observability marker for Akamai logs; does not affect routing/caching. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a test where both the activation POST and the latestActivation recovery lookup fail, so the handler falls through to the sanitized 502 — restoring diff coverage for the recover inner-catch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A just-queued Akamai activation appears in the activations list before its id is assigned, serializing as "atv_null" (which passes the general id regex but is not pollable). The activate recovery now returns activationId:null for any non-real (non-numeric) id, so the UI polls by network until a real atv_<digits> appears instead of polling an unpollable placeholder and 404-ing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Hey @ABHA61,
Verdict: Request changes - gist tarball dependency must be swapped before merge; baseVersion validation gap should be fixed.
Complexity: HIGH - medium diff with supply-chain pin, API surface changes, and multiple behavioral shifts.
Changes: Reworks Akamai OAE onboarding to use full-tree PUT with CUSTOM-default scope gate, conditional caching, activation recovery, and optional baseVersion (7 files).
Note: CI checks are currently failing - resolve before merge.
Must fix before merge
-
[Critical] Supply chain risk - Gist tarball dependency -
package.json:86@adobe/spacecat-shared-akamai-clientresolves from a personal Gist URL rather than the npm registry. The Gist owner can modify or delete the content; there is no independent provenance verification that the tarball matches the source in spacecat-shared PR 1825. The PR description acknowledges this is temporary, but this must NOT be merged in this state. Gate merge on the upstream package publishing. -
[Important]
baseVersionvalidation accepts 0 -src/controllers/llmo/llmo-akamai.js:592DECIMAL_INT_REis/^[0-9]+$/which matches"0". AfterNumber("0"), the code passes version 0 toclient.getRuleTree(propertyId, 0, ...). PAPI property versions start at 1, so this produces an opaque 404/500 rather than the intended clean 400. Fix: addif (Number(rawBaseVersion) < 1) return badRequest(...)after the regex check, mirroring theactivatehandler pattern. -
[Important] Activation recovery fires on any exception -
src/controllers/llmo/llmo-akamai.js:780The recovery block fires on any exception from
client.activate, not only timeouts or 422. If the POST fails for a genuine permission error (403) or rate-limit (429), recovery still querieslatestActivationand could match an unrelated earlier activation for the same version+network (e.g. submitted manually in Property Manager), returning falserecovered: true. Consider narrowing to specific error signatures (timeout string match + 422 status) or adding a recency/timestamp check on the recovered activation. -
[Important] Stale OpenAPI description for
insertIndex-docs/openapi/llmo-api.yaml:6489The spec still reads "Defaults to 0 (before everything)" but the code now defaults to LAST (
children.length) when no index is provided. This actively misleads consumers who rely on the spec. Fix: update to reflect the new default (appends after all existing children).
Non-blocking (4): minor issues and suggestions
- nit: Wildcard-wrapping heuristic (
String(ua).includes('*')) leaves values likeGPTBot*(trailing only) untouched rather than ensuring both leading and trailing wildcards -src/controllers/llmo/llmo-akamai-utils.js:104 - suggestion:
buildRuleTreePatchis now dead code in the controller (import removed) but still exported and tested - mark deprecated or remove in a follow-up - nit:
behaviorCachinghardcodesdefaultTtl: '1d'- document this choice so operators know AI-bot responses may be edge-cached up to a day when origin omits Cache-Control -src/controllers/llmo/llmo-akamai-utils.js:200 - suggestion: Recovery catch logs at
warnlevel when both the activation POST and the recovery probe fail - this double-failure warrantserrorfor alerting visibility -src/controllers/llmo/llmo-akamai.js:810
Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 0m 4s | Cost: $9.10 | Commit: 2f194a3aa5f03512e835b09b8945e388f7a11928
If this code review was useful, please react with 👍. Otherwise, react with 👎.
- deploy: reject baseVersion 0 (passes DECIMAL_INT_RE but PAPI versions start at 1) with a clean 400, mirroring activate. - activate recovery: only adopt a listed activation submitted within the last 5 minutes (isRecentlySubmitted) so a failed POST can't falsely match an unrelated older activation for the same version+network; log the double-failure at error. - behaviorCaching: document that defaultTtl 1d is the origin-omits-Cache-Control fallback, not the common path. - OpenAPI: insertIndex now documents the append-last default (was "0/first"). Addresses MysticatBot/Codex review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Follow-up commits: reject |
There was a problem hiding this comment.
Hey @ABHA61,
⚠ 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: Request changes - gist tarball dependency must be resolved before merge; the PR is no longer in draft.
Complexity: HIGH - large diff with supply-chain pin and API surface changes.
Changes: Reworks Akamai OAE onboarding to use full-tree PUT with CUSTOM-default scope gate, conditional caching, activation recovery with recency window, and optional baseVersion (7 files).
Note: Recommend a human read before merge - this change modifies a shared contract (docs/openapi/llmo-api.yaml). The bot review is a complement to, not a replacement for, a human read here.
Must fix before merge
- [Critical] Gist tarball dependency still in place on an open (non-draft) PR -
package.json:86(details inline)
Non-blocking (1): minor issues and suggestions
- suggestion: Recovery tests rely on wall-clock proximity to
Date.now()rather than a fake clock; boundary behavior (4:59 vs 5:01) is not directly exercisable withoutsinon.useFakeTimers-src/controllers/llmo/llmo-akamai.js:51
Previously flagged, now resolved
baseVersionvalidation now rejects < 1 with a clean 400 (mirrors activate handler) + regression test- Activation recovery narrowed with
isRecentlySubmitted(5-min recency window); double-failure upgraded tolog.error+ tests for stale/recent/placeholder/different-version/double-failure - OpenAPI
insertIndexdescription updated to document the append-last default defaultTtl: '1d'documented as the origin-omits-Cache-Control fallback
Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 2m 27s | Cost: $6.97 | Commit: 2c67abdf46a7c630f56986daaf621e283b161378
If this code review was useful, please react with 👍. Otherwise, react with 👎.
| "@adobe/mysticat-shared-seo-client": "1.3.1", | ||
| "@adobe/spacecat-helix-content-sdk": "1.4.33", | ||
| "@adobe/spacecat-shared-akamai-client": "1.1.0", | ||
| "@adobe/spacecat-shared-akamai-client": "https://gist.githubusercontent.com/ABHA61/8b997b63f4899a50df67073df4d2c1d2/raw/0adc628ab30acd38c16179f034bf5f9dac52ee9c/adobe-spacecat-shared-akamai-client-1.1.0.tgz", |
There was a problem hiding this comment.
issue (blocking): Supply chain risk - gist tarball dependency.
@adobe/spacecat-shared-akamai-client resolves from a personal Gist URL rather than the npm registry. The prior review flagged this as Critical; the author acknowledged it as a pre-merge blocker ("will swap once #1825 releases"). However, the PR has since moved from draft to open without resolving it - the structural safeguard preventing an accidental merge is gone.
The Gist owner can modify or delete the content at any time. There is no provenance verification, no CI, and no audit trail on the tarball. If this merges as-is, production builds fetch from a personal Gist URL.
Engagement with pushback: The author's commitment to fix before merge is understood and reasonable, but the PR is now in a mergeable state without a branch-protection rule or CI check preventing it. Either (a) move the PR back to draft until the swap is done, (b) add a CI check that fails on non-registry dependency URLs, or (c) swap to the published package now that spacecat-shared#1825 is available.
Removes the 5-minute recency guard from activation recovery. Re-activating a version that is already ACTIVE returns Akamai 422 already-activated; with the recency guard, an activation submitted >5min ago was rejected and surfaced as a false "Akamai activation failed", even though the version is live on the network. The version+network + in-flight/active status match is the correct guard: it means the desired end state exists, so report success regardless of age. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Hey @ABHA61,
⚠ 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: Request changes - gist tarball dependency must be resolved before merge; the PR is no longer in draft.
Complexity: HIGH - large diff with supply-chain pin and API surface changes.
Changes: Reworks Akamai OAE onboarding to use full-tree PUT with CUSTOM-default scope gate, conditional caching, activation recovery without recency gating, and optional baseVersion (7 files).
Note: Recommend a human read before merge - this change modifies a shared contract (docs/openapi/llmo-api.yaml). The bot review is a complement to, not a replacement for, a human read here.
Must fix before merge
- [Critical] Gist tarball dependency still pinned on a non-draft PR -
package.json:86(details inline)
Non-blocking (0): no minor issues or suggestions
No non-blocking findings on this increment.
Previously flagged, now resolved
baseVersionvalidation now rejects < 1 with a clean 400 + regression test- Activation recovery refined: recency window removed in favor of version+network+status match; rationale documented in code comments and tests updated
- OpenAPI
insertIndexdescription updated to document the append-last default
Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 1m 18s | Cost: $4.72 | Commit: a1ed81f7b210f207f5a45c718b8892ff3a29c468
If this code review was useful, please react with 👍. Otherwise, react with 👎.
| "@adobe/mysticat-shared-seo-client": "1.3.1", | ||
| "@adobe/spacecat-helix-content-sdk": "1.4.33", | ||
| "@adobe/spacecat-shared-akamai-client": "1.1.0", | ||
| "@adobe/spacecat-shared-akamai-client": "https://gist.githubusercontent.com/ABHA61/8b997b63f4899a50df67073df4d2c1d2/raw/0adc628ab30acd38c16179f034bf5f9dac52ee9c/adobe-spacecat-shared-akamai-client-1.1.0.tgz", |
There was a problem hiding this comment.
issue (blocking): Supply chain risk - gist tarball dependency remains unresolved.
@adobe/spacecat-shared-akamai-client still resolves from a personal Gist URL rather than the npm registry. The PR is no longer in draft, so no structural safeguard prevents an accidental merge.
The incremental code change (recency-window removal) is well-reasoned and the test coverage is adequate - this remains the sole blocker. Once spacecat-shared#1825 publishes and the dependency is swapped to the registry version, the PR is ready to merge.
The variable was declared and set on failover but never read — the cache key doesn't reference it, no rule consumes it, and the EO worker never sees PMUSER vars. It was only there for POC parity. Removing the setVariable + its declaration keeps the managed rule tree minimal; the Site Failover rule keeps just the GA failAction. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
adobe/spacecat-shared#1825 merged and released the client as 1.2.0. Swaps the temporary gist tarball for the published version and regenerates the lockfile — resolving the merge blocker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Hey @ABHA61,
Verdict: Approve - all prior blockers resolved, code is well-structured with comprehensive test coverage.
Complexity: HIGH - large diff with dependency upgrade, API surface changes, and multiple behavioral shifts.
Changes: Reworks Akamai OAE onboarding to use full-tree PUT with CUSTOM-default scope gate, conditional caching, activation recovery, and optional baseVersion (7 files).
Note: Recommend a human read before merge - this change modifies a shared contract (docs/openapi/llmo-api.yaml). The bot review is a complement to, not a replacement for, a human read here.
Non-blocking (6): minor issues and suggestions
- suggestion: OpenAPI response schema for
baseVersionstill reads "The latest version the new version was created from" but it may now be an explicit caller-supplied version - update to "The version the new version was created from" -docs/openapi/llmo-api.yaml - suggestion:
buildRuleTreePatchJSDoc still says "0 = before everything = default" but the default is now LAST (append) - stale comment in dead code -src/controllers/llmo/llmo-akamai-utils.js:493 - nit: Recovery logic does not explicitly assert
recovered.network === network- thelatestActivationcall is already scoped by network, but an explicit check would make the contract visible -src/controllers/llmo/llmo-akamai.js:789 - suggestion: Add a direct unit test for
buildRoutingRulewithcfg.addCaching = trueto verify the Caching behavior is positioned before cacheId - currently only tested through the integration-level controller test -test/controllers/llmo/llmo-akamai-utils.test.js - suggestion: Add a test for the scope gate when
getDefaultOriginSslreturns null/undefined (tree with no origin behavior) to exercise the "an unknown mode" error path -test/controllers/llmo/llmo-akamai.test.js - nit: Wildcard wrapping produces
**for an empty-string UA value - not exploitable since the defaults list is service-controlled, but a.filter(ua => String(ua).trim())before.map(...)would harden it -src/controllers/llmo/llmo-akamai-utils.js:99
Previously flagged, now resolved
- Gist tarball dependency swapped to
@adobe/spacecat-shared-akamai-client@1.2.0from npm registry baseVersionvalidation now rejects < 1 with a clean 400 + regression test- Activation recovery narrowed with version+network+status match; double-failure upgraded to
log.error+ tests for stale/recent/placeholder/different-version/double-failure - OpenAPI
insertIndexdescription updated to document the append-last default
Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 1m 16s | Cost: $7.68 | Commit: 68885463d989fe27be25ec5805550b9198561c51
If this code review was useful, please react with 👍. Otherwise, react with 👎.
# [1.669.0](v1.668.1...v1.669.0) (2026-07-20) ### Features * **llmo-akamai:** CUSTOM-default OAE onboarding (PUT deploy + caching/scope gates) ([#2850](#2850)) ([e67e9c5](e67e9c5)), closes [Hi#Level](https://github.com/Hi/issues/Level) [adobe/spacecat-shared#1825](adobe/spacecat-shared#1825)
|
🎉 This PR is included in version 1.669.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
What
Reworks the Akamai "Optimize at Edge" (BYOCDN) onboarding for CUSTOM-default properties.
Rule builder (
llmo-akamai-utils.js)*GPTBot*) so they match real agent strings, not only an exactGPTBot.cacheIdis always present.env.EDGE_OPTIMIZE_EDGE_DOMAIN(dev/stage/live.edgeoptimize.net; defaults to live);matchSan(*.edgeoptimize.net) covers all three.Controller (
llmo-akamai.js)updateRuleTree, pinnedruleFormat; plan uses the client'sdryRun).400) before creating a version (no orphan version).baseVersion(copy from a specific version; rejects0); caller email in the activation note.422 already-activated) — in both cases Akamai has already queued the activation, soactivaterecovers by reporting the in-flight/active activation for the same version+network instead of a false failure.Why
Validated end-to-end on
tokowaka-aem.stage.adobe.com: an OAE-rule Caching behavior on a property that (correctly) defaults HTML tono-storeoverrides it and makes the optimized path cacheable, so Akamai replays a stale/passthrough copy to bots (v40 = 0/20 optimized at IAD). Conditional caching + full-tree PUT + the CUSTOM scope gate + append-last ordering fix it — AI bots receivex-edgeoptimize-request-id+no-store, humans pass through.Design
High-Level Design: Optimize at Edge — Akamai BYOCDN Automation (HLD)
Depends on
adobe/spacecat-shared#1825 (merged, released as
@adobe/spacecat-shared-akamai-client@1.2.0) —defaultRuleHasCaching,getDefaultOriginSsl,updateRuleTree({ dryRun }).package.jsonnow pins the published1.2.0.Testing
Controller + utils unit tests green, lint clean, OpenAPI updated + valid.
🤖 Generated with Claude Code