Skip to content

feat(scaleway): a server gives up a private interface, and the pin moves to 2.83.0 - #780

Merged
stephrobert merged 1 commit into
mainfrom
fix/scaleway-serves-detach-private-network-interface
Sep 14, 2026
Merged

stephrobert merged 1 commit into
mainfrom
fix/scaleway-serves-detach-private-network-interface

Conversation

@stephrobert

Copy link
Copy Markdown
Owner

Provider 2.83.0 was published at 15:06 on 2026-09-14, three hours after 2.82.0's
pin landed, and every terraform destroy against this emulator broke:

501 Not Implemented: feint does not serve
/instance/v2alpha1/zones/fr-par-1/servers/{id}/detach-private-network-interface

fix(instance): detach private network interface before deleting it (upstream
#4354) sends a call this pack declined. The drift machinery behaved the way it
is built to: the failure named the missing path instead of answering something
wrong.

THE REASON THAT EXPIRED, AND THE HALF THAT KEEPS IT

The refusal read "no client this project drives reaches for these operations —
a claim that held for the whole API until provider 2.81.0 moved private network
interfaces and placement groups onto it". A sentence that already carried one
expiry date has collected a second.

Only the detach leaves the list. feint proxy recorded a full apply plus
destroy: 35 v2alpha1 calls, the detach twice, and
AttachServerPrivateNetworkInterface never. Serving a half because its other
half is served would be surface nothing drives, so the reason is split rather
than deleted.

IT DISSOCIATES RATHER THAN DELETES, AND THAT IS MEASURED

The first implementation reused releaseNIC and deleted. Every suite stayed
green. The recording shows why that was luck:

200  POST   …/detach-private-network-interface
200  GET    …/private-network-interfaces/{id}     <- still there
204  DELETE …/private-network-interfaces/{id}     <- the client deletes it
404  GET    …/private-network-interfaces/{id}

which is the upstream fix's own name spelled out as two calls. Deleting at the
detach answered the client's read with a 404, so it never sent its DELETE. Both
behaviours pass the suite; one matches what the client does. The SDK said the
same without any measurement: DetachAndDeletePrivateNetworkInterface exists
separately, which is only meaningful if the plain one leaves the interface
standing.

THREE VOCABULARIES THAT ARE NOT instance/v1's

Serving it needed the server rendered in v2alpha1's shape, which this pack did
not have — nineteen fields, taken from contracts/scaleway.json rather than from
memory. The contract check caught every divergence, and each is the same class:

server status    v1 `running`      v2alpha1 `started`
interface status                   `available`, never a server's word
volume type      v1 `sbs_volume`   v2alpha1 `sbs`

b_ssd and unified have no v2alpha1 spelling and answer
unknown_volume_type, the enum's own escape hatch, rather than a guessed
equivalence onto sbs: the products look alike and no measurement says they
are the same, which is exactly the invented format rule 4 forbids.

WHAT FALSIFY FOUND IN THIS WORK

Three tests were green for a reason that was not theirs:

  • the empty-identifier test asserted "not 200", so it could not tell a
    400 invalid_arguments from a 404, and an empty id resolves to nothing
    anyway. The guard was redundant; the test was weak.
  • the field test asserted PRESENCE, never whether a value belongs to its enum.
    Both vocabulary defects passed under it.
  • the enum test stayed green under the mutation that spoils the public
    interface, because the server had no public address: public_network_interface
    was null and the assertion never ran. The population trap, met in my own test.

Eight mutations bite now, including one that restores the delete-at-detach.

AND A GUARD I DISARMED WITHOUT NOTICING

Editing the decline reason moved a fragment that tools/falsify/specs/artefact-reasons.json
mutates, so a guard about artefacts printing stale reasons had quietly stopped
being measured. falsify:lint caught it; the fragment is retargeted on the
sentence as it stands today, and its three mutations bite again.

PROVEN

  • conformance:leg -- terraform, scw-cli and fields: green. fields is the
    only leg where the omission gate judges, and it judged the nineteen fields.
  • conformance:stacks, conformance:quickstart: green.
  • conformance:functional under incus-ovn, three passes, Scaleway and
    Outscale, no divergence between them: machines really running on the host,
    public paths, firewall in both directions, cross-VPC isolation, reboots,
    stop/start cycles, rule sets, teardown. Incus identical before and after.
  • The seven falsify specs the testplan named, plus the new one.
  • evidence.json records the operation as driven, with contract: clean.

Assisted-by: Claude Code (claude-opus-5)

🤖 Generated with Claude Code

…ves to 2.83.0

Provider 2.83.0 was published at 15:06 on 2026-09-14, three hours after 2.82.0's
pin landed, and every `terraform destroy` against this emulator broke:

    501 Not Implemented: feint does not serve
    /instance/v2alpha1/zones/fr-par-1/servers/{id}/detach-private-network-interface

`fix(instance): detach private network interface before deleting it` (upstream
#4354) sends a call this pack declined. The drift machinery behaved the way it
is built to: the failure named the missing path instead of answering something
wrong.

THE REASON THAT EXPIRED, AND THE HALF THAT KEEPS IT

The refusal read "no client this project drives reaches for these operations —
a claim that held for the whole API until provider 2.81.0 moved private network
interfaces and placement groups onto it". A sentence that already carried one
expiry date has collected a second.

Only the detach leaves the list. `feint proxy` recorded a full apply plus
destroy: 35 v2alpha1 calls, the detach twice, and
AttachServerPrivateNetworkInterface **never**. Serving a half because its other
half is served would be surface nothing drives, so the reason is split rather
than deleted.

IT DISSOCIATES RATHER THAN DELETES, AND THAT IS MEASURED

The first implementation reused releaseNIC and deleted. Every suite stayed
green. The recording shows why that was luck:

    200  POST   …/detach-private-network-interface
    200  GET    …/private-network-interfaces/{id}     <- still there
    204  DELETE …/private-network-interfaces/{id}     <- the client deletes it
    404  GET    …/private-network-interfaces/{id}

which is the upstream fix's own name spelled out as two calls. Deleting at the
detach answered the client's read with a 404, so it never sent its DELETE. Both
behaviours pass the suite; one matches what the client does. The SDK said the
same without any measurement: DetachAndDeletePrivateNetworkInterface exists
separately, which is only meaningful if the plain one leaves the interface
standing.

THREE VOCABULARIES THAT ARE NOT instance/v1's

Serving it needed the server rendered in v2alpha1's shape, which this pack did
not have — nineteen fields, taken from contracts/scaleway.json rather than from
memory. The contract check caught every divergence, and each is the same class:

    server status    v1 `running`      v2alpha1 `started`
    interface status                   `available`, never a server's word
    volume type      v1 `sbs_volume`   v2alpha1 `sbs`

`b_ssd` and `unified` have no v2alpha1 spelling and answer
`unknown_volume_type`, the enum's own escape hatch, rather than a guessed
equivalence onto `sbs`: the products look alike and no measurement says they
are the same, which is exactly the invented format rule 4 forbids.

WHAT FALSIFY FOUND IN THIS WORK

Three tests were green for a reason that was not theirs:

- the empty-identifier test asserted "not 200", so it could not tell a
  400 `invalid_arguments` from a 404, and an empty id resolves to nothing
  anyway. The guard was redundant; the test was weak.
- the field test asserted PRESENCE, never whether a value belongs to its enum.
  Both vocabulary defects passed under it.
- the enum test stayed green under the mutation that spoils the public
  interface, because the server had no public address: `public_network_interface`
  was null and the assertion never ran. The population trap, met in my own test.

Eight mutations bite now, including one that restores the delete-at-detach.

AND A GUARD I DISARMED WITHOUT NOTICING

Editing the decline reason moved a fragment that tools/falsify/specs/artefact-reasons.json
mutates, so a guard about artefacts printing stale reasons had quietly stopped
being measured. `falsify:lint` caught it; the fragment is retargeted on the
sentence as it stands today, and its three mutations bite again.

PROVEN

- conformance:leg -- terraform, scw-cli and **fields**: green. `fields` is the
  only leg where the omission gate judges, and it judged the nineteen fields.
- conformance:stacks, conformance:quickstart: green.
- conformance:functional under incus-ovn, **three passes**, Scaleway and
  Outscale, no divergence between them: machines really running on the host,
  public paths, firewall in both directions, cross-VPC isolation, reboots,
  stop/start cycles, rule sets, teardown. Incus identical before and after.
- The seven falsify specs the testplan named, plus the new one.
- evidence.json records the operation as driven, with `contract: clean`.

Assisted-by: Claude Code (claude-opus-5)
@stephrobert
stephrobert merged commit 980b765 into main Sep 14, 2026
31 checks passed
@stephrobert
stephrobert deleted the fix/scaleway-serves-detach-private-network-interface branch September 14, 2026 19:26
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