Skip to content

docs(agent-context): teach brownfield-adoption direction doctrine in migration skills#183

Merged
dmealing merged 1 commit into
mainfrom
docs/migration-direction-doctrine-skills
Jul 7, 2026
Merged

docs(agent-context): teach brownfield-adoption direction doctrine in migration skills#183
dmealing merged 1 commit into
mainfrom
docs/migration-direction-doctrine-skills

Conversation

@dmealing

@dmealing dmealing commented Jul 7, 2026

Copy link
Copy Markdown
Member

Intent

Fix the metaobjects agent-context skills so they teach the RIGHT direction for a brownfield migration/adoption. User-reported production failure: the skills were framed greenfield-only ('metadata is the spine, generated code is disposable'), which led an agent to change metadata, regenerate, and then force the existing downstream code to fix the resulting type errors as if they were bugs — the backward direction. The concrete disaster: a UUID column modeled field.string + @dbColumnType:uuid generated a String (not UUID), inherited via BaseEntity across 262 fields / 91 files of String<->UUID coercions; verify --db can't catch it because the column really is uuid. Correct doctrine (what this change encodes): on adoption onto existing working code / a live DB, author metadata AND tune codegen to REPRODUCE what the code already is (native types, column/table names, nullability), minimize churn to code the generator isn't replacing, ask the user when a modeling choice is ambiguous, and default to the least existing-code change; the only existing code that should change is the hand-written layer codegen replaces (parity-gate, then delete). Deliberate choices a reviewer should NOT flag: (a) the direction doctrine is intentionally CONDITIONAL on a brownfield predicate and reconciled with the existing greenfield model-first guidance (both must coexist — do not treat the greenfield 'disposable generated code' language as contradictory). (b) The UUID hardening reinforces an already-present field.uuid recommendation; a control-vs-treatment micro-test on Sonnet showed the UUID case is already self-correcting on capable models, so this is intentionally reinforcement + weaker-model insurance + an audit reclassification (advisory axis-I -> real axis-H2 finding), NOT a claimed behavior flip. The migration-DIRECTION doctrine was the piece that micro-tested as a clean RED->GREEN (control led with a greenfield remodel + schema/data migration; treatment matched existing storage with zero churn and deferred the modernization). (c) Only a project-local grep CI-ratchet lint was added as guidance; an actual semantic-lint subverb is intentionally left as a noted follow-up, not implemented here. (d) The SDK agent-context/ bundle copy is a gitignored build artifact, intentionally not committed; only the canonical agent-context/ source (5 files) + regenerated conformance goldens (24 files across 4 stacks) are committed. Scope: docs/skills only, no runtime or product code. Local gates already green: agent-context conformance + size-gate (always-on stayed within the 120-line budget) + capability-grounding = 42/42.

What Changed

  • Added a brownfield-adoption direction doctrine across the metaobjects agent-context skills (metaobjects-authoring, -audit, -codegen, -verify) plus a line in templates/always-on.md.mustache: when adopting onto existing working code or a live DB, author metadata and tune codegen to reproduce what the code already is (native types, column/table names, nullability), minimize churn, ask when a modeling choice is ambiguous, and change only the hand-written layer codegen replaces. The doctrine is gated on a brownfield predicate and left to coexist with the existing greenfield model-first ("generated code is disposable") guidance.
  • Hardened the UUID rule: reinforced the field.uuid recommendation and reclassified the field.string + @dbColumnType: uuid String↔UUID coercion case from an advisory audit finding to a real (axis-H2) finding, with an illustrative project-local grep CI-ratchet noted as guidance (a semantic-lint subverb left as a follow-up).
  • Regenerated the agent-context conformance goldens (.claude/skills/*/SKILL.md and .metaobjects/AGENTS.md / CLAUDE.md) across all four stacks so they match the updated canonical source. Local gates stayed green (agent-context conformance, size-gate within the always-on line budget, capability-grounding — 42/42).

Risk Assessment

✅ Low: Docs-only change to agent-context skills with regenerated conformance goldens; goldens are byte-in-sync with the canonical source, all conformance gates (drift, grounding, size) are satisfied, and no runtime or product code is touched.

Testing

Baseline: fresh checkout needed bun install (root) and the gitignored SDK agent-context bundle rebuilt via its bundle script — a setup step, not a product failure — after which the full agent-context suite is 42/42. The four gates the change targets (conformance golden byte-match, capability-grounding registry check, 120-line size-gate, vocabulary-drift scan) all pass. Beyond tests, I exercised the actual deliverable: I ran the real assemble() API for a realistic typescript+react+tanstack adopter stack (what meta init scaffolds) and captured the brownfield-adoption doctrine exactly as it reaches the end user — the always-on file now leads with "Metadata FOLLOWS the code" reconciled against the greenfield model-first guidance, and the UUID hardening, axis-H2 audit finding, CI-ratchet lint, and adoption-direction guardrail all render into the scaffolded skills. This is a documentation change with no rendered-UI surface, so the reviewer-visible artifacts are the assembled Markdown files themselves rather than screenshots. Transient build output was removed; the worktree is clean and evidence lives under /tmp.

Evidence: Scaffolded always-on AGENTS.md (what an adopter imports into their repo root)
# Working with MetaObjects in this project

> Stack: typescript server, react, tanstack client.

MetaObjects is a metadata standard: typed metadata in `metaobjects/` is the durable
spine; generated code is the disposable artifact. Regenerate with `npx meta gen`.

## Principles
- **Adopting onto existing code? Metadata FOLLOWS the code.** On a migration (existing working code / live DB), author metadata + tune codegen to *reproduce* what the code already is — native types (`field.uuid` when the code uses `UUID`, not `field.string`), names, nullability — so regen changes as little existing code as possible. The only existing code that should change is the hand-written layer codegen replaces; ask when a modeling choice is ambiguous. (Greenfield: model-first, below.)
- Pattern-derivable from metadata = codegen, never hand-write — FKs, CRUD, validators, finders, and the database schema and migrations. The schema is a disposable, generated artifact: change the metadata and regenerate, never hand-write SQL.
- The **live database** is a derived artifact too — never hand-apply a schema change to a running DB (ad-hoc `psql`/console `ALTER`/`CREATE`/`DROP`), not even to preview a column or unblock a boot. Apply schema only through `meta migrate` (metadata → DDL). A hand-applied change drifts the live DB from the metadata + migration history and collides at the next migrate/boot ("column already exists") — a state no migration can reproduce. Run `meta verify --db` after any DB-touching work to catch that drift early.
- Never hand-edit generated files — change the metadata and regenerate (three-way merge preserves hand-written regions).
- Use the generated constants for any string that names metadata.
- The loaded metadata model is READ-ONLY — never inject nodes or mutate the tree at load time (no "enrich the model on load" hooks). Need an extra field/column? Author it in the metadata, or derive it during codegen (read the metadata, emit output). Mutating the loaded model makes it diverge from what's declared — a bad practice reserved for very rare cases.

## Authoring rules you must not violate
- Nodes are fused-key maps: `{"<type>.<subType>": { ... }}` (e.g. `{"field.string": {"name": "email"}}`) — never split the type and subtype into separate keys.
- Attribute names are unique within a node; for multi-value use one array attr (`@values: [...]`).
- An inline `@maxLength: 50` equals an `attr` child of the same name — never write both.
- Package paths use `::` (`acme::common::id`).

## Keep all MetaObjects ports in sync
MetaObjects ships as separate packages per language on DIFFERENT version lines (npm/PyPI/NuGet `0.x`/`1.x`, Maven Central `7.x`/`8.x`). Because the numbers differ by ecosystem, a stale port is INVISIBLE — an old TS client next to a new Java backend *looks* fine. Ports are only truly in sync when every one implements the same **Metamodel spec version** (`metamodelVersion`, on the registry manifest). Upgrade ALL ports together and confirm they land on the same Metamodel version; a lagging port silently disagrees on vocabulary + wire behavior.

## Going deeper (Claude Code)
For authoring, codegen, runtime/UI, prompts, verify, or adoption-audit work, use the
matching `metaobjects-*` skill — its body links the `references/<lang>.md` fragment
installed for this project's stack.

These `metaobjects-*` skills are plain, inspectable Markdown reference docs (no tools
or hooks) generated by MetaObjects for this project's stack — safe to read and edit;
re-run the agent-context scaffold to refresh them.
Evidence: Scaffolded always-on CLAUDE.md variant
# Working with MetaObjects in this project

> Stack: typescript server, react, tanstack client.

MetaObjects is a metadata standard: typed metadata in `metaobjects/` is the durable
spine; generated code is the disposable artifact. Regenerate with `npx meta gen`.

## Principles
- **Adopting onto existing code? Metadata FOLLOWS the code.** On a migration (existing working code / live DB), author metadata + tune codegen to *reproduce* what the code already is — native types (`field.uuid` when the code uses `UUID`, not `field.string`), names, nullability — so regen changes as little existing code as possible. The only existing code that should change is the hand-written layer codegen replaces; ask when a modeling choice is ambiguous. (Greenfield: model-first, below.)
- Pattern-derivable from metadata = codegen, never hand-write — FKs, CRUD, validators, finders, and the database schema and migrations. The schema is a disposable, generated artifact: change the metadata and regenerate, never hand-write SQL.
- The **live database** is a derived artifact too — never hand-apply a schema change to a running DB (ad-hoc `psql`/console `ALTER`/`CREATE`/`DROP`), not even to preview a column or unblock a boot. Apply schema only through `meta migrate` (metadata → DDL). A hand-applied change drifts the live DB from the metadata + migration history and collides at the next migrate/boot ("column already exists") — a state no migration can reproduce. Run `meta verify --db` after any DB-touching work to catch that drift early.
- Never hand-edit generated files — change the metadata and regenerate (three-way merge preserves hand-written regions).
- Use the generated constants for any string that names metadata.
- The loaded metadata model is READ-ONLY — never inject nodes or mutate the tree at load time (no "enrich the model on load" hooks). Need an extra field/column? Author it in the metadata, or derive it during codegen (read the metadata, emit output). Mutating the loaded model makes it diverge from what's declared — a bad practice reserved for very rare cases.

## Authoring rules you must not violate
- Nodes are fused-key maps: `{"<type>.<subType>": { ... }}` (e.g. `{"field.string": {"name": "email"}}`) — never split the type and subtype into separate keys.
- Attribute names are unique within a node; for multi-value use one array attr (`@values: [...]`).
- An inline `@maxLength: 50` equals an `attr` child of the same name — never write both.
- Package paths use `::` (`acme::common::id`).

## Keep all MetaObjects ports in sync
MetaObjects ships as separate packages per language on DIFFERENT version lines (npm/PyPI/NuGet `0.x`/`1.x`, Maven Central `7.x`/`8.x`). Because the numbers differ by ecosystem, a stale port is INVISIBLE — an old TS client next to a new Java backend *looks* fine. Ports are only truly in sync when every one implements the same **Metamodel spec version** (`metamodelVersion`, on the registry manifest). Upgrade ALL ports together and confirm they land on the same Metamodel version; a lagging port silently disagrees on vocabulary + wire behavior.

## Going deeper (Claude Code)
For authoring, codegen, runtime/UI, prompts, verify, or adoption-audit work, use the
matching `metaobjects-*` skill — its body links the `references/<lang>.md` fragment
installed for this project's stack.

These `metaobjects-*` skills are plain, inspectable Markdown reference docs (no tools
or hooks) generated by MetaObjects for this project's stack — safe to read and edit;
re-run the agent-context scaffold to refresh them.
Evidence: Brownfield doctrine as it lands in .claude/skills/* (authoring direction + UUID smell, codegen match-the-code, verify CI-ratchet lint, audit axis-H2 + guardrail)
# Evidence — brownfield-adoption doctrine as scaffolded into an adopter repo

Assembled via the real `assemble()` API (`typescript + react + tanstack` stack).
Byte-identical to what `meta init` installs under `.claude/skills/*`.


---

## authoring/SKILL.md → Adopting onto an existing codebase (direction doctrine)

## Adopting onto an existing codebase — metadata FOLLOWS the code

The principle above is the **greenfield** default: declare the model, generate the
code. **Adoption reverses the direction.** When you are introducing MetaObjects into
a project that already has **working code and/or a live database** — a migration, not
a fresh start — the existing code and schema are the specification, and the metadata's
first job is to **reproduce them**. You are documenting a reality that already runs, not
redefining it. (The metadata is still the durable spine *going forward*; only the
*direction of fit on the way in* changes. Once adopted, the greenfield rules resume.)

**The observable predicate:** does working code or a populated schema already exist for
what you're modeling? If yes, you are in adoption mode and these rules apply.

**Author metadata to match what the code ALREADY IS — not what you'd design fresh.**
Read the existing code and schema *first*, then model to reproduce them:
- The **native types the code uses** are the spec — model `field.uuid` when the code
  uses `UUID`, `field.decimal` when it uses `BigDecimal`, etc. Do **not** pick a
  metadata shape whose generated type differs from the type already in use (that is the
  exact mistake that turned a `UUID` column into a `String` and forced coercions across
  hundreds of fields — see the UUID rule below).
- The existing **column names, table names, nullability, and field shapes** are the
  spec — carry them over (`@column`, `@table`, `@required`, `@maxLength`) so the
  generated schema matches the live one and `verify --db` is clean.

**Customize the CODEGEN to match the existing code before you change the existing code.**
If generated output doesn't match the code's shape (naming, file layout, imports,
signatures), **tune the generator/template/config to reproduce it** — that is the
intended adoption path (owned generators, `outputPattern`, naming strategy — see the
`metaobjects-codegen` skill), **not a hack**. Reshaping working call sites to satisfy
the generator's defaults is the *last* resort, not the first.

**Minimize churn to code the generator is not replacing.** The ONLY existing code that
should change is the hand-written layer codegen now **owns** (the hand-rolled
CRUD/DTO/validator/mapper you're deleting) — parity-gate it, then delete it; that is the
point of adopting. Everything else — call sites, business logic, adjacent modules —
stays untouched. **If a metadata choice would force a wide edit across code the
generator isn't replacing, treat that as a signal the metadata is modeling the wrong
thing** and re-check it against the code, rather than editing the code to fit the
metadata.

**When a modeling choice is genuinely ambiguous, ask — don't pick the churnier option.**
If two metadata shapes both fit the existing code and they imply different amounts of
existing-code change, surface the tradeoff to the user rather than choosing silently.
**Default to the choice that changes the least existing code.**

Do NOT: change metadata, regenerate, and then work through the resulting compile/type
errors in the existing code as if they were bugs. On an adoption those "errors" are the
metadata failing to match the code — fix the *metadata* (or the codegen customization),
not the code.


---

## authoring/SKILL.md → UUID smell (hardened rule)

**UUID columns are `field.uuid` — `field.string` + `@dbColumnType: uuid` is a forbidden smell.**
A UUID column is modeled with the **`field.uuid`** subtype (native `UUID` / `Guid` /
`uuid.UUID`, canonical lowercase-hex on the wire). Do **not** reach for `field.string` +
`@dbColumnType: uuid`: that pairing makes the *DB column* a uuid but generates a **`String`
property in code**, so every consumer must coerce `String ↔ UUID` at every boundary. It reads
"correct" because `verify --db` passes (the column really is uuid) — the defect is invisible to
the schema gate and only shows up as wrong native types rippling through the code. Left in a
`BaseEntity`, it is inherited by every `id`/`tenantId`/FK — hundreds of fields across a repo, a
staged multi-PR migration to undo. So:

`` `json
{ "field.uuid": { "name": "id" } }                                  // ✅ native UUID
{ "field.string": { "name": "id", "@dbColumnType": "uuid" } }       // ❌ generates String over a uuid column
`` `

The `field.string` + `@dbColumnType: uuid` form is legitimate **only** in the genuinely rare
case where your code truly wants a *string-typed* value stored in a uuid column (you handle the
uuid as text everywhere and never as a native UUID). That is an explicit, justified exception —
not a default, and never the way to model an identifier. When adopting an existing schema whose
code already uses `UUID`, `field.uuid` is the match-the-code choice (see "Adopting onto an
existing codebase" above).

**Timestamps — instant by default, `@localTime` for naive wall-clock (ADR-0036 Wave 2).**
`field.timestamp` is **instant / timezone-aware by default** (Postgres `timestamptz`;
native `Instant` / `DateTimeOffset` / aware `datetime`) — use it for created/updated/event
times. Add **`@localTime: true`** only for a genuine naive wall-clock value (a store-open
time, a birthday-with-time, a recurring local schedule) → `timestamp without time zone`.
Never use `@dbColumnType: timestamp_with_tz` — it is **retired**; timezone-awareness now
lives in `field.timestamp` (instant by default) + the `@localTime` naive opt-out.


---

## codegen/SKILL.md → make codegen match the code

### Adopting onto existing code — make codegen match the code, not the code match codegen

On a **brownfield adoption** (existing working code / live schema — see
`metaobjects-authoring` → "Adopting onto an existing codebase"), the goal of codegen is to
**reproduce the shape the code already has** so the generated output drops in with minimal
churn. When generated output doesn't match — different names, file layout, imports, or
signatures than the existing code — **customize the codegen to match the existing code first**,
using the à-la-carte layers, `outputPattern`/target layout, naming strategy, template
customization, and owned/custom generators described here. That is the intended adoption path,
**not a hack** — the whole point of owned generators + three-way merge is to shape output to
your codebase. Reshaping working call sites to fit the generator's defaults is the **last**
resort, and only for the layer codegen is actually replacing (the hand-rolled CRUD/DTO/mapper
you're deleting behind a parity gate). If matching the existing shape would require a genuinely
hacky generator contortion, that is the moment to **ask the human** which side should give —
don't silently churn the existing code.


---

## verify/SKILL.md → what verify can't catch (CI ratchet lint)

## What `verify` can't catch — semantic mismodeling (add a CI ratchet lint)

The three subverbs check that derived artifacts *match the metadata*. They do **not**
check that the metadata *models the right thing* — so a semantically wrong metadata
choice that is internally consistent passes clean. The canonical case: a UUID column
modeled **`field.string` + `@dbColumnType: uuid`**. The generated property is a `String`,
the DB column is genuinely `uuid`, so **`verify --db` passes** while every consumer coerces
`String↔UUID` and the native type is wrong throughout the code (see `metaobjects-authoring`
→ the UUID smell). No drift subverb can see it, because nothing has drifted — the model
itself is wrong.

For semantic invariants like this, add a **project-local CI ratchet lint** over the
metadata sources — a grep-level gate is enough:

`` `


---

## audit/SKILL.md → axis H2 (real finding, reclassified from advisory)

- [ ] **H2. Wrong native type — `field.<x>` + `@dbColumnType` that hides the real type
  (CORRECTNESS-ADJACENT finding, NOT advisory axis-I).** The headline instance is a **UUID
  column modeled `field.string` + `@dbColumnType: uuid`**: the DB column is uuid but the
  generated property is a **`String`**, so the code coerces `String↔UUID` at every boundary
  and the native type is wrong everywhere the field is used. `verify --db` **cannot** catch it
  (the column type matches), so it hides in plain sight. When it sits in a shared
  `BaseEntity`/`BaseAuditedEntity`, **every inheriting `id`/`tenantId`/FK is wrong** — count the
  blast radius (grep every `field.string` paired with `@dbColumnType: uuid`; it is often
  hundreds of fields). This is a **real finding**, not a modernization nudge: recommend
  `field.uuid` and flag it as a **staged migration** (re-typing `id`/FK ripples through
  repositories, finders, and call sites) — tier by blast radius, not buried as advisory. The
  ONLY non-finding is a field the code genuinely handles as a *string* over a uuid column
  (explicitly justified). Report the total pair count so the migration has a completion
  criterion (see the CI ratchet gate in `metaobjects-verify`).


---

## audit/SKILL.md → adoption-direction guardrail

- **Adoption direction — metadata follows the code.** This is a brownfield project: existing
  code and the live schema are the spec. Every `metadata_sketch` must **reproduce the code's
  existing native types, names, and nullability** (model `field.uuid` where the code uses
  `UUID`, carry over `@column`/`@table`/`@required`) and every cutover must **minimize churn to
  code the generator is not replacing** — customize the codegen to match the existing shape
  before proposing edits to working call sites. A sketch that would re-type or rename working
  code the generator isn't replacing is modeling the wrong thing; when a choice is ambiguous,
  flag it for the human rather than proposing the churnier option. (Full doctrine:
  `metaobjects-authoring` → "Adopting onto an existing codebase".)
- **Parity-gate every cutover** — prove behavior-equivalent before deleting hand-written code; generated schemas are often looser.
- **Verify, don't assume** — read the code behind a grep hit.
- **Verify the DB artifact, not just the types** — the contract may claim a column the view DDL dropped.
Evidence: End-user-surface assertions from the live assemble() run
=== End-user-surface assertions ===
PASS always-on AGENTS.md teaches 'Metadata FOLLOWS the code'
PASS always-on AGENTS.md prefers field.uuid over field.string for UUID
PASS always-on stays within 120-line budget
AGENTS.md line count: 33 / 120 budget
Evidence: Full agent-context gate result after bundling
bun test v1.3.8
42 pass
0 fail
208 expect() calls
Ran 42 tests across 10 files.

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ℹ️ agent-context/skills/metaobjects-verify/SKILL.md:91 - The illustrative CI-ratchet grep in metaobjects-verify uses grep -Ez ... &#34;field\.string&#34;[^}]*&#34;@dbColumnType&#34;[^}]*&#34;uuid&#34;. Because -z makes [^}]* stop at the first }, a field.string node whose object contains a nested child (e.g. a children array or view.* sub-object closing a }) before @dbColumnType will NOT match — a false negative. That quietly undermines the doc's own claim that the ratchet 'can't go green until the last offending field is migrated' / is a 'permanent backstop': offenders with intervening braces could remain while the gate reads green. The existing caveat ('tighten to per-node scope if a coarse co-occurrence match is too broad') addresses over-matching but not this under-match. Consider adding a one-line note that a per-node (JSON-aware) matcher is needed for a reliable completion criterion. Advisory only — it is explicitly labeled 'Illustrative' guidance, not committed CI.
✅ **Test** - passed

✅ No issues found.

  • agent-context conformance
  • capability-grounding
  • size-gate
  • vocabulary drift
  • full agent-context suite (42 tests)
  • end-to-end bundle assembly
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…d UUID rule

The metaobjects-* skills were framed greenfield-only ("metadata is the spine,
generated code is disposable"), which licensed a backward migration loop: change
metadata → regenerate → treat the resulting compile/type errors in existing code
as bugs to fix. On an adoption onto existing working code / a live DB the
direction is the reverse — metadata FOLLOWS the code.

- authoring: new "Adopting onto an existing codebase — metadata FOLLOWS the code"
  section (conditional on the brownfield predicate, reconciled with model-first):
  author metadata + tune codegen to reproduce the existing native types, names,
  and nullability; minimize churn to code the generator isn't replacing; ask on
  ambiguity, default to least existing-code change. Hardened the UUID row + a
  smell callout: NEVER field.string + @dbColumnType:uuid (generates a String over
  a uuid column, forcing coercions; verify --db can't see it). field.uuid is the
  match-the-code choice.
- audit: promoted UUID-as-string from non-failing advisory (axis I) to a real
  correctness-adjacent finding (axis H2) with blast-radius counting; added an
  adoption-direction guardrail on every proposed cutover.
- codegen: "make codegen match the code, not the code match codegen" — customize
  owned generators / outputPattern / naming to reproduce the existing shape before
  editing working call sites.
- always-on: one concise direction bullet (within the 120-line size gate).
- verify: "what verify can't catch — semantic mismodeling" + a project-local CI
  ratchet lint (grep-fail on field.string paired with @dbColumnType:uuid) as the
  migration's completion criterion and a permanent backstop.

Goldens regenerated across all four stacks; agent-context conformance, size-gate,
and capability-grounding suites green (42/42). The SDK agent-context bundle is a
build-time artifact (gitignored) and is not committed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VXidfNN755CyTZTg1w8UV
@dmealing
dmealing merged commit d64a927 into main Jul 7, 2026
1 check passed
@dmealing
dmealing deleted the docs/migration-direction-doctrine-skills branch July 7, 2026 11:34
dmealing added a commit that referenced this pull request Jul 7, 2026
Ships the updated agent-context skills (brownfield-adoption direction doctrine +
hardened UUID rule, #183) — bundled into the SDK, delivered via the CLI. Isolated
patch: the other 12 npm packages stay 0.15.14; PyPI/NuGet/Maven unaffected (no
runtime/codegen change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VXidfNN755CyTZTg1w8UV
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.

1 participant