Skip to content

refactor(serenity): skip the brand's own primary domain in brand URLs#2748

Merged
rainer-friederich merged 4 commits into
mainfrom
feat/resolve_urls
Jul 6, 2026
Merged

refactor(serenity): skip the brand's own primary domain in brand URLs#2748
rainer-friederich merged 4 commits into
mainfrom
feat/resolve_urls

Conversation

@rainer-friederich

@rainer-friederich rainer-friederich commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

1. Abstract

Fixes the duplicate brand-domain entry a Serenity brand shows in Semrush by not adding the brand's own primary domain as a brand URL (it already lives on the project's own-brand benchmark). Replaces the earlier "resolve brand URLs to their scheme-less form before writing" approach, which a prod re-capture showed was invalid.

2. Reasoning

A brand shows its primary domain twice in Semrush (e.g. lovesac.com): once as the own-brand benchmark's own primary_url (which Semrush stores scheme-less), and once as an explicit website brand-URL row (https://lovesac.com) the write path added. A prod re-capture (2026-07-06) of the live contract established two facts that invalidate the original url/resolve approach this PR opened with:

  • create_brand_urls REQUIRES a literal https:// value — it 400s on the scheme-less resolve primary_url ('url' tag) and on http:// ('startswith' tag). So the resolved scheme-less form cannot be written as a brand URL at all.
  • create_benchmarks already normalizes the benchmark domain server-side, so url/resolve was redundant for the one field where a scheme-less value is valid.

Because a brand_urls row must keep its https:// scheme and the benchmark domain is scheme-less, the two can never collapse — the only way to remove the visible duplicate is to not emit the primary domain as a brand URL in the first place.

3. High-level overview of the changes

Before: every brand-URL entry was pushed to the own-brand benchmark, and (in the reverted revision) each was first run through url/resolve.

After: the brand's own primary domain is dropped from the website set; every other brand URL (secondary sites, socials, earned) is written verbatim as an https:// value.

  • collectBrandUrlEntries takes the project's own-brand domain and skips any website URL whose host matches it. The match is host-wise via the existing normalizeBenchmarkDomain, so both the apex and www. forms of the primary are dropped.
  • The market-create path passes the brand's domain; the brand-edit re-sync passes each project's own domain.
  • Removes the resolveUrl transport method and the resolveBrandUrlEntries step — brand URLs are now written exactly as collected (the upstream stores them as-is and does not collapse www.-vs-apex, so no normalization is applied).

Net user-visible effect: a Serenity brand no longer double-lists its primary domain in Semrush; socials and any secondary sites are unchanged.

4. Required information

5. Affected / used mysticat-workspace projects

6. Additional information outside the code

The live contract behind this change was re-captured against prod adobe-hackathon.semrush.com on 2026-07-06: a read-only url/resolve edge matrix, plus a write-probe of create_brand_urls / create_benchmarks on a throwaway benchmark created and deleted in an authorized test workspace. That probe is what established the https://-only requirement and the server-side benchmark-domain normalization. The paid customer's live data was inspected read-only (no writes) and confirmed to carry the duplicate this PR prevents.

7. Test plan

  • (a) Local: unit tests for collectBrandUrlEntries (primary-domain skip in apex + www forms; all-kept when no primary given) and both write paths (create attaches the non-primary set; edit diffs verbatim with no churn). The serenity IT exercises the create/edit write paths against the mock.
  • (b) Per-env: after merge, verify on dev by creating (or editing) a Serenity brand whose website equals its benchmark domain and confirming the AIO benchmark shows the primary domain only once (no second https://<domain> brand-URL row), while socials/secondary sites still attach.

8. Deployment & merge order

🤖 Generated with Claude Code

… writing them

Brand URLs were pushed to Semrush AIO benchmarks verbatim (e.g.
https://www.lovesac.com), which does not match the canonical benchmark Semrush
stores (lovesac.com) — creating duplicate www-vs-apex entries. Resolve website
URLs through Project Engine's GET /v1/url/resolve and write the returned
canonical primary_url instead (serenity-docs#25 §2).

- bump @adobe/spacecat-shared-project-engine-client 1.7.1 -> 1.8.0 (adds the
  url/resolve endpoint to the typed client + mock)
- rest-transport: add resolveUrl(primaryUrl) (typed GET /v1/url/resolve)
- brand-urls: add resolveWebsiteEntries — resolves type:'website' entries only
  (social/earned are identity handles, passed through), falls back to the raw
  url on is_valid:false (never writes the empty value), re-de-dups www-vs-apex
  collapses, and lets transport errors propagate (CREATE best-effort / EDIT
  hard-fail semantics unchanged)
- wire it into attachBrandUrlsToProject (CREATE) and syncBrandUrlsAcrossMarkets
  (EDIT, before the diff) so the diff compares Semrush's canonical form on both
  sides — idempotent after the first (self-correcting) migration re-sync
- toEntry's https-only guard is left unchanged: resolution is a post-collection
  step, so it only ever validates raw inputs

Verified: unit tests (resolveWebsiteEntries, attach/sync normalization,
idempotency, legacy-migration diff); serenity IT green against the 1.8.0 mock;
type-check + lint clean.

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

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@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: Request changes - one convention fix needed before merge.
Complexity: HIGH - medium diff; dependency risk signal.
Changes: Adds URL canonicalization via Semrush's url/resolve before writing brand URLs, preventing www-vs-apex duplicates on both the create and edit paths (6 files).
Note: CI checks are currently pending.

Must fix before merge

  1. [Important] Version specifier ^1.8.0 is inconsistent with the project's exact-pin convention - package.json:96 (details inline)
Non-blocking (4): minor issues and suggestions
  • suggestion: Resolve brand-wide website URLs once before the per-market loop in syncBrandUrlsAcrossMarkets to avoid redundant HTTP calls (the resolve result is deterministic and region-less) - src/support/serenity/brand-urls.js:409
  • nit: De-dup in resolveWebsiteEntries keys on url alone; a type-qualified key (${type}:${url}) would prevent a theoretical cross-type collision - src/support/serenity/brand-urls.js:155
  • nit: The warn message says "url/resolve returned is_valid:false" even when the actual condition is is_valid:true with empty primary_url - src/support/serenity/brand-urls.js:131
  • suggestion: Add a unit test for resolveUrl returning null/undefined to document the optional-chaining defense - test/support/serenity/brand-urls.test.js

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

Comment thread package.json Outdated
@MysticatBot MysticatBot added ai-reviewed Reviewed by AI complexity:high AI-assessed PR complexity: HIGH labels Jul 3, 2026
…e-once cache)

- package.json: pin @adobe/spacecat-shared-project-engine-client to exact 1.8.0
  (was ^1.8.0), matching every other spacecat-shared dependency
- resolveWebsiteEntries: accept an optional per-sync resolve cache and share one
  across the syncBrandUrlsAcrossMarkets market loop — website urls are region-less,
  so each distinct one is resolved once instead of per market
- correct the fallback warn message ("no usable primary_url"), which also covers
  the defensive is_valid:true-but-empty-primary_url case, not just is_valid:false
- tests: null/undefined resolve response, shared-cache resolve-once (helper + the
  cross-market sync path)

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

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@rainer-friederich

Copy link
Copy Markdown
Contributor Author

Addressed the review in b7ae9ad.

Must fix:

  • [Important] ^1.8.0 inconsistent with the exact-pin convention — done. Pinned to exact 1.8.0 and regenerated the lockfile.

Non-blocking:

  • suggestion: resolve website URLs once before the per-market loop — done. resolveWebsiteEntries now takes an optional per-sync resolve cache, and syncBrandUrlsAcrossMarkets shares one across markets; since website urls are region-less, each distinct one is resolved a single time (cache hit on markets 2..N). Added a helper test and a two-market sync test asserting one resolve call.
  • nit: warn message inaccurate for the is_valid:true + empty primary_url case — done. Reworded to "url/resolve returned no usable primary_url — keeping raw url" (and it now warns only on a cache miss, so a bad url warns once, not per market).
  • suggestion: test for resolveUrl returning null/undefined — done. Added a unit test pinning the optional-chaining defense.
  • nit: type-qualified de-dup key (${type}:${url}) — declined. resolveWebsiteEntries intentionally mirrors collectBrandUrlEntries' url-only de-dup: a Semrush brand URL is unique by its url, so keying on ${type}:${url} would let the same url be written twice under different types (Semrush treats it as one entry). Post-resolution a canonicalized website (scheme-less apex, e.g. acme.com) also can't collide with a social/earned handle (a full https:// profile url), so the theoretical cross-type collision can't occur.

@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 - all prior findings addressed, no new blocking issues.
Complexity: HIGH - medium diff; dependency risk signal.
Changes: Adds URL canonicalization via Semrush's url/resolve before writing brand URLs, preventing www-vs-apex duplicates on both the create and edit paths (6 files).
Note: CI checks are currently pending.

Previously flagged, now resolved

  • Version pin now exact 1.8.0 (was ^1.8.0), consistent with all other shared dependencies
  • Shared resolve cache across markets eliminates redundant HTTP calls for region-less website URLs
  • Warn message reworded to accurately cover both is_valid:false and empty primary_url cases
  • Unit test for resolveUrl returning null/undefined now present
Non-blocking (3): minor issues and suggestions
  • nit: No test for cache-hit with a null (unresolvable) cached value - a test pre-seeding the cache with new Map([['https://bad.com', null]]) and asserting no re-resolve and no re-warn would pin the once-per-url contract explicitly - src/support/serenity/brand-urls.js:157
  • nit: attachBrandUrlsToProject has no integration-level test verifying the de-dup effect of resolution (two raw URLs collapsing to one canonical), though the unit-level coverage in resolveWebsiteEntries tests is solid - test/support/serenity/brand-urls.test.js
  • suggestion: Sequential resolution in resolveWebsiteEntries (one await per URL) is fine for current cardinality (1-3 website URLs per brand) but would serialize round-trips if a brand ever carries 20+ URLs - a bounded Promise.all would be a future optimization if that threshold is reached - src/support/serenity/brand-urls.js:130

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

@MysticatBot MysticatBot added complexity:high AI-assessed PR complexity: HIGH and removed complexity:high AI-assessed PR complexity: HIGH labels Jul 3, 2026
The new rest-transport resolveUrl wrapper had no direct unit test — the
brand-urls tests stub it — so codecov/patch flagged the uncovered method.
Add a test asserting it GETs /v1/url/resolve with the primary_url query param
and returns the body, mirroring listGlobalAiModels.

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

Copy link
Copy Markdown
Contributor Author

Follow-up in 2a5e049 (test-only): the new resolveUrl transport wrapper had no direct unit test (the brand-urls tests stub it), so codecov/patch flagged it. Added a rest-transport test asserting it GETs /v1/url/resolve?primary_url=… and returns the body — mirrors listGlobalAiModels. Re-requesting so the approval lands on HEAD.

@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 - test-only commit, no new concerns.
Complexity: HIGH - medium diff; dependency risk signal.
Changes: Adds a unit test for the resolveUrl transport wrapper to close the Codecov patch coverage gap (7 files).
Note: CI checks are currently pending.


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

@rainer-friederich
rainer-friederich enabled auto-merge (squash) July 6, 2026 06:03
@rainer-friederich
rainer-friederich merged commit a064a84 into main Jul 6, 2026
20 checks passed
@rainer-friederich
rainer-friederich deleted the feat/resolve_urls branch July 6, 2026 06:13
solaris007 pushed a commit that referenced this pull request Jul 6, 2026
# [1.624.0](v1.623.0...v1.624.0) (2026-07-06)

### Features

* **serenity:** resolve brand URLs to Semrush's canonical form before writing them ([#2748](#2748)) ([a064a84](a064a84)), closes [Hi#level](https://github.com/Hi/issues/level)
@solaris007

Copy link
Copy Markdown
Member

🎉 This PR is included in version 1.624.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

rainer-friederich added a commit to adobe/spacecat-shared that referenced this pull request Jul 6, 2026
…10, slim CR16 (#1772)

<!-- mysticat-pr-skill -->

## 1. Abstract
Re-vendors the refreshed Semrush Project Engine public swagger in the
project-engine-client package and reconciles the correction overlay
against it, retiring the corrections the refresh has caught up on.

## 2. Reasoning
Semrush shipped a new revision of the Project Engine public swagger.
Several of our overlay corrections (which align the vendored spec with
the live API) exist only because the vendored spec lagged the live API;
once upstream ships the same shape, the correction becomes a redundant
no-op that the overlay freshness gate rejects. Re-vendoring lets us drop
that now-dead maintenance and pull in the new upstream surface, while
keeping the corrections upstream still hasn't fixed.

## 3. High-level overview of the changes
The overlay drops from 16 corrections to 14; the vendored swagger and
the generated TS/pydantic types are regenerated from the new upstream
revision.

- CR9 (`AISettings.primary_url`) — now present upstream, dropped.
- CR10 (`AIOBenchmarkWithCounters.primary_url` + `root_domain`) — now
present upstream, dropped.
- CR16 — `GET /v1/url/resolve` and its response schema
(`model.ResolveURLResponse`) are now vendored natively. The correction
collapses from "add the path + add a custom schema" to a single action
marking the three always-present fields (`domain`, `primary_url`,
`is_valid`) `required` (upstream still omits that, and the mock always
populates them — the same faithful-`required` convention as CR5). The
internal type reference is renamed `model.UrlResolveResponse` ->
`model.ResolveURLResponse`.
- CR1–CR8 and CR11–CR15 are unchanged — the refresh did not fix them.

The refresh also brings new upstream surface into the generated types:
new endpoints (`managed-ai-models`, `crawls/stop`,
`apply_interactive_unignore_issue_rules`), new fields (`primary_url` on
`ProjectRequest`/`ProjectUpdateRequest`/`AISettings`,
`main_brand`+`primary_url` on `AIOBenchmarkRequest`), a breaking reshape
of `ApplyInteractiveIgnoreIssueRulesRequest`, and removal of the
Site-Intelligence `allow_more_parallel_instances` endpoint +
`SIProjectSettingsRequest`. None of the removed/reshaped shapes are
referenced by the mock or client source, so nothing in this package
breaks.

The renamed resolve response type is internal to this package: the
merged consumer keys off the path string `/v1/url/resolve`
(openapi-fetch), not the type name, so it is unaffected and will pick up
the new types on the next client release.

## 4. Required information
- Spec: adobe/serenity-docs#25
- Other: consumer already merged —
adobe/spacecat-api-service#2748 ; original CR16
endpoint + mock (released as 1.8.0) —
#1771

## 5. Affected / used mysticat-workspace projects
- `spacecat-api-service` — consumed / contract: uses `url/resolve` via
the typed client (merged in PR 2748). This PR renames the resolve
response type internally; the consumer keys off the path, not the type
name, so it is not broken and only re-bumps the client on the next
release.
- `mysticat-data-service` — related, not touched: the Serenity migration
CLI performs the same `url/resolve` canonicalization on its own write
path.

## 6. Additional information outside the code
- Ran `npm run generate` end to end against the re-vendored spec:
swagger2openapi conversion, the overlay apply (all remaining actions
applied, none reported stale), and the TS + pydantic codegen all
completed.
- Booted the Counterfact mock and exercised the resolve endpoint through
the typed client (canonicalization, `is_valid:false` empty-string
fallback at HTTP 200, and the missing-vs-empty `primary_url` behaviour),
confirming Counterfact response validation accepts the now-`required`
`model.ResolveURLResponse`.

## 7. Test plan
- (a) Local: the overlay freshness gate (asserts every remaining
correction still applies and still changes the converted spec), the mock
factory/context/url-resolve unit tests, and the resolve e2e against the
booted mock were run locally and are green. Type-check against the
regenerated types passes.
- (b) Per-env: no service or public-API change here — this is a shared
client package. After merge and release, bump
`@adobe/spacecat-shared-project-engine-client` in the consumers and
confirm `url/resolve` still resolves against the released mock image
(the api-service serenity IT harness pins the mock tag to the installed
client version).

## 8. Deployment & merge order
- Related (not blocking):
adobe/spacecat-api-service#2748 — the resolve
consumer, already merged on client 1.8.0. It keeps working on 1.8.0; it
picks up the renamed resolve type only when re-bumped to the release
this PR produces.

Ordered sequence: merge + release this PR, then re-bump the client in
consumers at their own pace.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rainer-friederich rainer-friederich changed the title feat(serenity): resolve brand URLs to Semrush's canonical form before writing them refactor(serenity): skip the brand's own primary domain in brand URLs Jul 6, 2026
rainer-friederich added a commit to adobe/spacecat-shared that referenced this pull request Jul 13, 2026
…1778)

<!-- mysticat-pr-skill -->

## 1. Abstract
The vendored Project Engine mock now enforces the live Semrush
validation on `POST .../brand_urls`: a brand URL must be a literal,
lower-case `https://` URL, and the mock answers a non-conforming entry
with the same 400 (and the same go-validator tag) the live gateway
returns, instead of silently accepting it.

## 2. Reasoning
While reworking the serenity-docs#25 brand-URL feature, a prod
write-probe showed `create_brand_urls` REQUIRES a literal `https://`: a
scheme-less value 400s on the go-validator `url` tag, and a valid
non-https URL (`http://`, `ftp://`) 400s on the `startswith=https://`
tag. The mock accepted all of these, so an integration test could go
green over a write the live gateway rejects — exactly the fidelity gap
that let the earlier "resolve brand URLs to their scheme-less form
before writing" mis-design ship (the scheme-less resolve value cannot be
written as a brand URL at all).

Because the point of this gate is fidelity, the contract was probed
rather than inferred: ~40 edge cases were POSTed to the live API and the
mock reproduces the observed matrix, including the places where go's
semantics differ from JavaScript's and a `URL`-based check silently gets
the wrong answer.

## 3. High-level overview of the changes
Before: the mock's `POST .../brand_urls` handler created a row for any
`{ url, type }` entry, regardless of scheme, and always returned 200.

After: each entry's `url` is validated against the live contract before
any row is created. A non-conforming entry short-circuits the whole
batch with the gateway's 400 `BasicResponse`, naming the tag live names.
`BrandURLRequest.URL` carries `required,url,startswith=https://`,
evaluated in that order:

- `required` — an empty (or null/missing) value; reported before `url`
is ever evaluated.
- `url` — the value must parse, under go's rules: a non-empty scheme
plus a host, a fragment, or opaque content. A scheme-less value
(`lovesac.com`, `www.x.com`, `//x.com`), a scheme with only a rooted
path (`https:/x.com`), a bare `https://`, and any value containing
whitespace fail here.
- `startswith=https://` — a literal, case-SENSITIVE prefix check against
the raw value, reached by anything that parsed. `http://`, `ftp://`, an
upper- or mixed-case scheme (`HTTPS://X`, `Https://x`), every `file:`
form, and the hostless-but-opaque forms (`mailto:`, `tel:`,
`https:x.com`) fail here — not on `url`.

A conforming value is accepted and stored verbatim (no scheme or `www.`
normalization), unchanged from today; an upper-case host, an IDN host,
an IP, and a port/path/query are all accepted, as is `https://#frag` (a
non-empty fragment satisfies `url` even with no host). The batch is
atomic: one bad entry creates nothing.

The check lives in a pure `brandUrlHttpsTag` helper exposed on the
request context (same convention as the existing `resolveUrl` / `tagId`
lib helpers), so it is unit-tested independently of the
coverage-excluded route handler. It reproduces go's parse by hand rather
than delegating to JavaScript's `URL`, whose WHATWG parsing disagrees
with go exactly where it matters: it trims surrounding whitespace, and
it reports no host for the opaque forms go accepts.

## 4. Required information
- Jira / issue: serenity-docs#25 —
adobe/serenity-docs#25
- Other: write-probe of the live `create_brand_urls` contract
(2026-07-13, throwaway benchmarks in a dev workspace, cleaned up in the
same run)

## 5. Affected / used mysticat-workspace projects
- spacecat-api-service (consumer) — its serenity brand-URL write path
(PR #2748) writes only `https://` brand URLs after the
skip-primary-domain redesign; this mock keeps its integration tests
honest to the live 400.
- mysticat-data-service (consumer) — the serenity_migration CLI (PR
#737) likewise writes only `https://` brand URLs; same fidelity
guarantee.

## 6. Additional information outside the code
The contract was captured by a live write-probe against prod
`adobe-hackathon.semrush.com` on 2026-07-13: throwaway competitor
benchmarks were created in the LLMO-Dev-2 dev sub-workspace, ~40
candidate values were POSTed one per request, the accept/reject verdict
and go-validator tag were recorded for each, and every benchmark and row
created was deleted in the same run. No customer data was touched.

Two results are worth calling out because they contradict what the code
would otherwise assume:
- go-validator's `startswith` is `strings.HasPrefix` against the raw
value, so an upper-case scheme (`HTTPS://…`) is rejected, while an
upper-case host (`https://EXAMPLE.NET`) is fine. Only the scheme must be
literal.
- every `file:` form clears the `url` tag and fails on `startswith` —
including `file://` and `file:///`, which go-validator's current source
would reject on `url`. The deployed validator predates that path check,
so the probed behaviour is what the mock models; the code says so at the
point it matters.

## 7. Test plan
- Unit: the probed matrix is recorded case by case against the pure
helper — accepts, the case-sensitivity of the scheme, scheme-less,
non-https, opaque/hostless, `file:`, degenerate and whitespace forms,
`required`, and the non-string unmarshal failure. The package's 100%
branch-coverage gate is met.
- E2E (`test:e2e`, which boots the real mock server): the route answers
the live 400 and tag over real HTTP for the scheme-less, `http://`,
upper-case-scheme, `mailto:` and empty-url forms, a missing url is
rejected by request validation, the batch is proven atomic (a good entry
alongside a bad one is not created), and the existing create/list/delete
e2e (which uses an `https://` URL) still passes through the new gate.
- No environment rollout applies — this is a test-only mock artifact in
a shared package; it ships in the next
`spacecat-shared-project-engine-client` release and is picked up by
consumers when they bump.

## 8. Deployment & merge order
- serenity-docs#25 redesign is a coordinated set: this mock fix hardens
the contract that spacecat-api-service PR #2748
(adobe/spacecat-api-service#2748) and
mysticat-data-service PR #737
(adobe/mysticat-data-service#737) rely on. It is
independent to merge (the consumers do not import it at runtime), but
merging it first and cutting a release lets those PRs' integration tests
run against the tightened mock. Suggested order: this PR (+ release) →
then #2748 / #737.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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 AI-assessed PR complexity: HIGH released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants