fix(codegen/digitalocean): address every generated model by a real identifier (swamp-club #2834) #461
Loading…
Reference in a new issue
No description provided.
Delete branch "2834-2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes swamp-club #2834.
Problem
updateandsyncon generated DigitalOcean models read the resource ID from stored state using an identifier that codegen derived by stripping underscores from the path parameter (access_keybecameaccesskey). Where that field did not exist and the response had noid, the models requested/v2/.../undefined, andsyncrecorded live resources asnot_found.The fix is in
codegen/digitalocean/; everything undermodel/digitalocean/is regenerated output.Changes, grouped by cause
1. Identifier resolution (
pipeline.ts): the identifier is now matched against the GET response fields: overrides,IDENTIFIER_MAP, an exact match, a match ignoring case and underscores, thenid. This fixes seven models:space_key(access_key), the reported bugreserved_ipv6(ip)byoip_prefix(uuid)function_namespace(namespace)action_gateway_mcp_server(serverRef)action_gateway_output_view(view_id)monitoring_sink(see 3)Ten more models that only worked because of the
existing.idfallback (load_balancer,kubernetes_cluster,ssh_keyand others) now resolve toiddirectly. Their output changes only cosmetically.2. Generation guard: a model whose identifier is neither a response field nor
idis reported as a generation error. Its previous file is kept, and DigitalOcean generation now exits 2, which the nightly job already expects. The guard foundreserved_ipv6, which my triage audit had missed.3. Monitoring sink (
IDENTIFIER_FROM_ARGS): the API never returns the sink UUID, according to the spec.getpersists its argument, andsync/updatecarry it over. Aftercreate,syncfails with "run get with the resource ID first".4.
get/synconly with a GET-by-id endpoint:action_gateway_sessionlosesgetandsync. Neither ever worked: the API has no single-session read endpoint.5. Clear errors instead of
/undefined:updateandsyncin every model throw a named error when stored state has no identifier. This is why about 50 models change uniformly.6. Create-response siblings (
createEnveloped): with an explicit envelope key, the session keepsmcpUrland the connection keepsauthorization. These are a create-time snapshot that the firstsyncreplaces. Thededicated_inferencetoken secret is deliberately still not stored.7.
checkExists: it reads by name only when the name is the identifier, sobyoip_prefixkeeps its list-filter check.8. Stable
get/deletearguments:byoip_prefixandreserved_ipv6keep takingid.9. Orphan pruning for DigitalOcean, Hetzner and Tailscale (AWS, GCP, Cloudflare and Vercel already prune). This removes the stale
security_secret.ts, which calls an endpoint no longer in the spec and was absent from the manifest. Pruning is skipped when generation reports errors, and runs after files are written. Hetzner and Tailscale have no diff.Separate commit:
3d47c85contains unrelated upstream spec drift (insight_notification_channel) from the baseline regeneration, isolated so the fix diff stays reviewable.Verification
pipeline_test.ts,generatedModel_test.ts(runs the real pipeline on a minimal spec and drives the generated models against a stubbed fetch), andcommands/generate_test.ts. Snapshots were updated for the identifier changes only.GET /v2/spaces/keys/<access_key>, andsynckeeps the key's state.2026.10.06.2).67ce99e: verify-build 14/14; code-review and adversarial-review pass. Three review rounds of non-blocking findings were fixed ind301acf,63403f8and67ce99e.Assumptions and known limits
function_namespaceusesnamespace, based on the spec'sfn-xxxxexample.reserved_ipv6usesip, inferred from the path parameter.not_foundmarkers written by earlier versions under a stripped key (for example{ lbid: X }) no longer resolve. A re-syncof such a record stops with "run get with the resource ID first" instead of re-requesting the 404.authorization(connect_url,verification_code) is stored unvaulted until the firstsync.🤖 Generated with Claude Code
Review findings on the previous commit: - Droplet's create response is a oneOf ({ droplet } or { droplets }), which flattening merged into a two-key "envelope". Skip oneOf/anyOf responses, and require the resource key's object to share fields with the GET resource. - sync carried the connection's create-time authorization (pending status, verification code) forward forever. Siblings are now a create-time snapshot that the first sync replaces with live state; only an identifier persisted from get arguments is carried over. - Fix the carry-over line's indentation inside sync, and leave *_test.ts files alone when pruning orphan models. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>Second-round review findings: - A flat create response with a nested object sharing a field name ({ id, name, region: { name } }) could pick region as the envelope key. The key must now carry the resource's identifier, and an identifier at the top level marks the response as flat. - Run the identifier guard for resources with update but no GET-by-id, whose update reads the identifier from stored state. - Remove the temp dirs the end-to-end tests create. No generated output changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>