Skip to content

Queue: snapshot data-loss revert, three accepted-then-ignored params, and a diff-scoped lint gate - #2417

Draft
agbishop wants to merge 259 commits into
mainfrom
chore/queue-2026-08-11
Draft

Queue: snapshot data-loss revert, three accepted-then-ignored params, and a diff-scoped lint gate#2417
agbishop wants to merge 259 commits into
mainfrom
chore/queue-2026-08-11

Conversation

@agbishop

@agbishop agbishop commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Eleven commits off the follow-up queue. Every issue was spot-checked against live code before an agent was spent on it, which turned out to matter — two of the queued issues were already fixed.

Fixes

apigateway snapshot version — data loss (cb188a8a7). apigatewaySnapshotVersion went 1→2 in d39bf33 alongside a purely additive Tags *tags.Tags json:"tags,omitempty" on the nested stageSnapshot. An older snapshot still decodes fine with Tags zero-valued, so the bump bought nothing — but Restore discards on any version mismatch, resetting the registry and all nine dirty tables. Every instance with a persisted apigateway snapshot would have lost its state on the first start after that commit.

TestSnapshotVersionGuard did not catch it: version comparison lived only inside branches keyed on the field list changing, so version-only drift fell through silently — and the drift was real, source at 2 while the golden still said 1. Split into a pure diffSnapshots with a default: branch, so this now fails loudly instead of riding along on the next -update.

Two apigateway fixtures pinned "version":2 literally. With the constant at 2 they passed — through the discard path, not the restore path. They now pin 1 and exercise the real one.

ec2 RunInstances silent clamp (e44858734). The backend clamped count to 1000 and carried on, so cloudformation and tests calling it directly got fewer instances than requested and were told it succeeded. Now errors. The bound was also reported as InvalidParameterValue, framing gopherstack's own allocation-safety cap (CodeQL alert #253) as a malformed request; AWS documents ResourceCountExceeded for exactly this — "more instances than AWS allows in a single request... separate from your individual resource limit". EC2 models no typed exceptions in the SDK, so the code is verified against the API error-code reference and cited in errors.go.

datasync ServerHostname, all three location types (609864859, 4983d442e). NFS, then SMB and ObjectStorage. ServerHostname wasn't declared at all, so a hostname change reported success while LocationUri kept pointing at the old server. Each URI is rebuilt in the shape its own Create produces — these differ (nfs://host/subdir, smb://host/subdir, object-storage://host/bucket/subdir), and bucket is preserved from stored state since UpdateLocationObjectStorageInput has no BucketName member. AWS shipped the capability on all three at once (SDK CHANGELOG:268). The SMB and ObjectStorage PARITY rows had claimed wire: fixed ... FIXED this sweep while the member was missing.

databrew CreateJob (f735a8a3e) and workspaces image ops (973aa011e, b4682808b). Both accepted references to resources that were never created. Validation runs before any write, so a rejected call leaves nothing behind — the workspaces tests prove that directly by asserting the ID counter advances by exactly one across a rejected create, rather than arguing it from code order.

CreateWorkspaceImage was worse than unvalidated: it took workspaceId as _ /*workspaceId*/ and discarded it, though the handler had been threading it through all along.

CopyWorkspaceImage is the one deliberately left partly open: this service runs one backend per (account, region), b.images is flat and storedImage has no region field, so a genuine cross-region copy's source lives somewhere this instance cannot see. It validates only when SourceRegion is empty or matches; rejecting cross-region would be more restrictive than AWS. A test pins that as a choice.

Existing tests across the two services created resources against IDs that were never created — asserting behaviour the real services reject. They now create the referenced resource first rather than having the fix weakened around them.

Tooling

make lint-changed (c3d844000). Every per-change gate here has been scoped to a fixed directory, so nothing covered test/ — which is how a govet shadow in test/integration/datasync_test.go reached a commit and was only caught by CI's repo-wide run at merge time. The new gate resolves the actual diff to package directories: working tree unioned with branch-vs-merge-base, since verifying before committing and verifying at commit time need different halves. Verified by reintroducing that exact shadow — caught, exit 1.

gendocs silently dropped PARITY entries (29d3136fc). entryLineRe required a bare identifier for the key, so every family key naming several operations or carrying a parenthetical — AddPermission/RemovePermission, Database/TableMetadata (Get/List) — was skipped without a word. The operations badge moves 6111 → 6163 and 49 generated files change; none of it is new work, it is documentation that was written and not being read. Widening was checked against every <prefix>: { in services/*/PARITY.md: 165 additional distinct keys match, all legitimate, nothing spurious.

The silence was the real defect. A looser detector now reports entry-like lines that fail to parse, with file and line. Sixteen exist today (commas, *, ->) and were previously invisible; filed as gopherstack-42va. Warnings are non-fatal on purpose — ParseParityFile promises graceful degradation, and CI's docs job already fails on generated diff.

Docs

guardduty PARITY.md (3ab51d46a). Each status claim was re-verified against current code before being recorded, not copied from the commit message. GetRemainingFreeTrialDays stays graded partial, not ok — it computes a real value under the right shape, but features[] can only report the three always-on base sources. Three implemented operations had no ops-table row at all; that's the +3 in the operations badge (6108→6111), not new work. ListCoverage's filter is recorded as a gap and deliberately not built — nothing holds coverage-resource state, so it would filter a permanently-empty list and read as working.

apigatewayv2 basepath transforms (572c89ee9). Test-only. prepend had no assertion on the resulting route keys, which is how a review misread it as accepted-then-ignored. Now covers all four modes against a spec with a /v1 base path and one with none, for both operations, asserting route keys rather than status codes.

Gates

Gate Result
go build ./... clean
go vet ./... clean
golangci-lint run ./... 0 issues
go test ./... 204 packages ok
CI on this PR all shards green — see below
make check-pins 161/161
make docs + regen committed
test/terraform see below

The terraform suite times out locally as one process (25m, zero --- FAIL lines — it panics on the timer with cases still mid-flight). CI shards it 8×15m, so a single local run is roughly 8× a CI chunk. CI settled it: terraform-tests, all four integration-tests shards, all four unit-tests shards, lint, e2e-tests, modernize, govulncheck and codeql (go) all passed. The local timeout was machine capacity, not a regression.

Queue triage

  • gopherstack-66dr (route53resolver Filters) closed with no code change — already fully implemented in the same PR the follow-up was filed against.
  • gopherstack-jni0 narrowed rather than closed. My first pass on this was wrong: I grepped validateBasepath, saw only validation, and reported that basepath was accepted then ignored. It is not — prepend is implemented in applyOpenAPIToAPI (handler_apis.go:322-324) and applied by both ImportApi and ReimportApi. Only split falls back to ignore, and that was already documented honestly. It stays unimplemented deliberately: the SDK models the enum values but defers the semantics to prose, so building it would mean guessing at client-observable routing. The route-key transforms for all four modes are now pinned by tests so prepend can't regress silently.

Filed

gopherstack-2vgi (ec2 outpost fixed reservation — no local CodeQL to prove a tighter shape), gopherstack-42va (16 PARITY keys with commas/*/-> that still don't parse, now at least warned about).

Both gopherstack-7xcw and gopherstack-plmb were filed and then fixed in this same PR.

Two commit trailers name issue IDs that do not exist — 4983d442e says Closes gopherstack-2xhy. I misread bd create output and invented the ID; the real issue is gopherstack-7xcw, closed correctly. Recording it here rather than rewriting pushed history.

Needs a human decision

gopherstack-ylyb — during PR #2414 a subagent dismissed CodeQL alert 254 via gh api PATCH without being asked. The SRP reasoning holds: x is a transient protocol intermediate, only the verifier persists, and a slow KDF would structurally break every real-SDK login. But v = g^x mod N is stored at rest, so a store leak plus known salt/pool/username permits an offline dictionary attack — the KDF-hardness CodeQL asks for is precisely what SRP lacks. That makes the honest label "true positive, unfixable without breaking the emulated protocol" rather than "false positive". No alert state was touched during this review.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: beeb1bb4-41ca-4336-b57a-84174e3675b1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@agbishop

Copy link
Copy Markdown
Collaborator Author

📊 Code Coverage Report

Metric Value Status
Total Coverage 0.0%
0.0%
75.0%
0.0%
84.4%
New Code Coverage N/A (0/0 stmts)

Tip

This project maintains a minimum coverage threshold of 85%. Maintain or improve coverage on new code to ensure long-term stability.


Last updated: Tue, 11 Aug 2026 21:26:21 GMT

Witness Patrol and others added 27 commits August 11, 2026 16:31
…Image against images that do not exist

The siblings left out of 973aa01. Both take a SourceImageId that was never
checked, and both document ResourceNotFoundException
(aws-sdk-go-v2/service/workspaces@v1.73.1 deserializers.go:772 and :1636).

CreateUpdatedWorkspaceImage is validated unconditionally — same account and
region, no complication.

CopyWorkspaceImage is validated only when SourceRegion is empty or matches this
backend's own region. This service instantiates one InMemoryBackend per
(account, region) (provider.go:26-28), b.images is a flat table, and
storedImage carries no region field — so a genuine cross-region copy's source
image lives in a backend instance this one cannot see. Rejecting it would make
gopherstack more restrictive than real AWS, which is the worse bug. The
cross-region path stays deliberately unvalidated and a test pins that as a
choice rather than an oversight.

sourceRegion had been discarded as `_ /*sourceRegion*/` despite the interface
naming it; it is now threaded through and used.

Both checks run before createImageLocked, so a rejected call consumes no
identifier — asserted via the shared nextID counter advancing by exactly one
across a rejected attempt.

One existing test was passing for the wrong reason: TestDescribeImageAssociations_Validation
asserts a missing AssociatedResourceTypes is rejected, but its ImageId came
from an unvalidated copy that would now fail, so the assertion could have held
on an empty ImageId instead. It creates a real source image first.

Closes gopherstack-plmb

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ot a bare identifier

entryLineRe required `[A-Za-z0-9_]+` for the key, but real family keys name
several operations at once or carry a parenthetical — AddPermission/RemovePermission,
Database/TableMetadata (Get/List), Create/UpdateConfigurationTemplate response
shape. Every one of those was skipped without a word, so README family and
operation totals undercounted. The operations badge moves 6111 -> 6163; none of
that is new work, it is documentation that was already written and not being
read.

The key class now also accepts '/', '()', '-' and space, while keeping the
`:\s*\{` anchor that does the real disambiguating. Widening was checked against
every `<prefix>: {` occurrence in services/*/PARITY.md: 165 additional distinct
keys match, all of them legitimate names, and nothing that previously failed to
match as an entry now matches spuriously.

The silence was the actual defect, so a looser possibleEntryRe now detects
lines that look like entries but do not parse, and gendocs logs each with its
file and line. Sixteen such lines exist today — keys using commas, '*' or '->'
that are deliberately outside the parsing charset. They were invisible before
and are now reported on every run.

Warnings are non-fatal on purpose. ParseParityFile's contract is to degrade
gracefully rather than error, and CI's docs job already fails on any generated
diff, so a hard exit here would turn prose formatting in a PARITY.md note into
a blocking gate.

Closes gopherstack-udc7

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
29d3136 made these visible: fifteen entry keys used commas, '*' or '->',
which the parser deliberately excludes because accepting them would let it
match wrapped note prose containing ": {" and invent entries. They logged a
warning on every run but were still missing from the ops and family totals.

Fixed by renaming the keys rather than loosening the parser — commas become
slashes, '->' becomes "to", 'Describe*DetectionJob' becomes
'DescribeDetectionJob-family'. Where the key was carrying an enumeration, it
moves into the note: iam's five-operation list is now
'tag-cleanup-on-delete (5 resource kinds)' with the operations named in the
note text, so nothing a reader relies on is lost.

No status token changed — the added and removed wire/errors/state/persist/status
values are identical. This is a naming change only.

Fourteen of the sixteen warnings are gone. The remaining one is a false
positive and is left alone: services/rds/PARITY.md's 'leaks' family entry is
well-formed, but 'leaks' is also a reserved top-level key (parser.go:57), so
matchEntry rejects it and warnUnparsedEntry reports it. Filed separately.

Closes gopherstack-42va

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… was run

The 2026-07-25 audit diffed against v1.51.11 while go.mod pins v1.56.4, so
"every wired field diffed" was true of the wrong SDK. Re-derived the gap from
the pinned version rather than trusting the issue's list, and by diffing the
two SDK versions' member sets directly: it is exactly six fields, no more.

Two have a real input member to source a value from, so they are modelled on
the domain type and echoed exactly as supplied, never defaulted when absent:
ServerlessCache.NetworkType (CreateServerlessCacheInput.NetworkType,
serializers.go:6709 — create-only, no Modify member) and
ReplicationGroup.Durability (Create serializers.go:6506, Modify :8171).

The other four have no input member anywhere — StorageEncryptionType is
KMS-key-state-derived, EffectiveDurability is resolved server-side from engine
and cluster mode, and Snapshot.Durability comes from a source replication group
this model does not track. They are present on the wire structs as omitempty
and deliberately never populated. A fabricated encryption type or durability a
client can read and act on is worse than an absent field; this follows the
FullEngineVersion precedent already set here.

The wire tests assert on the raw XML rather than the SDK-parsed value, so a
field that serialises as an empty element instead of being omitted is caught —
a parsed zero value looks identical either way.

elasticacheSnapshotVersion stays at 1. Both new domain fields are additive
omitempty on structs that persist whole, and bumping for an additive field
discards every persisted snapshot (see cb188a8 earlier in this branch).

Closes gopherstack-31dm

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ticache fields

Operations badge 6163 -> 6169: fourteen family entries that the parser was
skipping on a key-charset technicality now count, plus the elasticache
additions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…guration

The audit that claimed CreateScheduledQuery and GetScheduledQuery modelled the
full DestinationConfiguration was run against v1.80.0 while go.mod pins
v1.81.1, which added LookupTableConfiguration as an alternative to
S3Configuration (types.go:778, type at :1561). All five members are
client-supplied — roleArn and tableName required, description, kmsKeyId and
tags optional — so every one is stored and echoed verbatim; nothing here needed
modelling shape-only.

S3Configuration is genuinely no longer required: validateDestinationConfiguration
(validators.go:2451) recurses into whichever member is non-nil and never checks
that at least one is set. A config with neither is accepted, and a test pins
that rather than leaving us stricter than the real API.

The three operations that carry the destination — CreateScheduledQuery,
GetScheduledQuery, ListScheduledQueries — pass the struct through whole, so
adding the field was sufficient. UpdateScheduledQuery is untouched: the real
input is a full replace including DestinationConfiguration while this backend
only accepts state, which is a separate pre-existing gap already tracked.

cwlSnapshotVersion stays at 1 — the field is additive omitempty and old
snapshots decode with it absent.

Closes gopherstack-09o8

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…er the audit

Both audits ran against a stale sdk_module pin, so "every wired field diffed"
was true of the wrong SDK.

mediatailor: AdsPersonalizationConcurrency and AdsPersonalizationTimeouts
(api_op_PutPlaybackConfiguration.go:58 and :63) fell outside extractExtraConfig's
fixed fourteen-key allowlist and were silently discarded, which falsified the
round-trip fidelity claim outright.

Rather than adding two keys to the list, the list is inverted: extractExtraConfig
now passes through everything except the four members the handler reads by name.
That closes the recurrence class — the next sub-config AWS adds survives without
touching this file. It is a small change only because these sub-configs were
already stored as decoded-JSON pass-through rather than typed structs.

The tradeoff is that an unrecognised key now round-trips instead of being
dropped. Real MediaTailor would ignore it, so this is slightly over-permissive
— but a client using the AWS SDK can only serialise modelled members, so it is
reachable only by a hand-rolled HTTP caller, and silently eating fields the SDK
does model is the worse failure. No test pins the unknown-key behaviour, so
this stays a judgement call rather than something entrenched.

DualStackPlaybackEndpointPrefix and DualStackSessionInitializationEndpointPrefix
(types.go:1049,1053) are response-only with no input member. They are modelled
on the struct and never populated — an invented endpoint prefix a client might
actually dial is worse than an absent field — so PutPlaybackConfiguration stays
wire: partial rather than being claimed whole.

mediaconvert: MaximumConcurrentFeeds (api_op_CreateQueue.go:47) is threaded
through Create and Update. No equivalent mechanism fix applies there —
createQueueInput and updateQueueInput are hand-modelled typed structs, so every
accepted field must be declared and there is no allowlist to invert.

Neither snapshot version constant is bumped; both stay at 1.

Refs gopherstack-gt9o

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…aconvert parity updates

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eserved word

services/rds/PARITY.md has both a genuine top-level `leaks:` section at column
0 and an indented `leaks:` family entry. matchEntry rejected any line whose key
was reserved regardless of indent, while isBlockTerminator only accepts a match
at column 0 — so the indented entry fell through both, counted as neither, and
was dropped from the families total. Since 29d3136 it also produced a warning
on every run, about a line that is not malformed.

matchEntry now rejects a line only when isBlockTerminator would claim it. That
ties the two functions together by construction, so no line can fall through
both, and it generalises: the same collision was waiting for any service naming
a family `gaps`, `protocol` or `gaps`-adjacent.

Indentation is the only workable discriminator here. The obvious alternative —
that a section header carries no brace on its own line — is false: rds's real
`leaks:` header is written `leaks: {status: ..., note: "..."}`, brace-identical
to a family entry.

A 0-space entry whose key is reserved is still read as that key's section
header. At column 0 the two forms are genuinely indistinguishable, and
parseFrontmatter re-reads the line as the scalar field, so the content becomes
LeaksStatus rather than being lost. The existing tolerance for 0-space entries
with non-reserved keys (services/mwaa, services/rekognition) is unaffected —
isReservedKey never applied to those.

Families across all 159 PARITY.md files go 1016 -> 1017, and the false warning
count goes 1 -> 0.

Closes gopherstack-jw5s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…difyClientProperties wiping the others

Continues the stale-pin sweep. Both services were audited against an older SDK
than go.mod pins — the cache still holds ssoadmin@v1.38.0 and workspaces@v1.68.3
alongside the pinned v1.43.1 and v1.73.1, and the fields in question do not
exist in the older ones.

The workspaces half turned up a bug the issue did not mention:
ModifyClientProperties replaced the whole stored struct on every call, so
setting one property silently cleared every other. The real operation is a
partial update. It now merges, leaving an omitted field at its previous value.

ClientExperiencePolicy (types.go:269) and LogUploadEnabled (:275) are both
threaded; the latter was unwired too. ClientExperiencePolicy is deliberately
unvalidated: unlike its neighbours LogUploadEnabled and ReconnectEnabled, which
have generated enum types with Values(), it is a bare *string with no @enum
trait. The FORCE_CLASSIC/FORCE_UI_2026/USER_CHOICE values in its doc comment
are illustrative, so rejecting anything else would be stricter than AWS.

ssoadmin: PermissionSetsEnabled (api_op_DescribeInstance.go:77) is stored as a
*bool, so an instance that never set it stays nil and is omitted rather than
reported as a fabricated false. AWS documents that it cannot be disabled once
enabled, but that is prose rather than an SDK-pinned constraint, so both values
are accepted verbatim.

InstanceMetadata.Regions is populated from real AddRegion state via ListRegions.
PrimaryRegion is modelled shape-only and never set: nothing in this backend can
source it, since RegionMetadata.IsPrimaryRegion is always false here.

Neither snapshot version constant is touched — workspaces stays 1, ssoadmin
stays 2.

workspaces' clientProperties map is pre-existing ephemeral state that was never
in backendSnapshot, so the new fields inherit that gap rather than creating one.
A round-trip test was written, confirmed to fail against that pre-existing
non-persistence, and reverted rather than expanding scope; recorded in
PARITY.md instead.

Refs gopherstack-gt9o

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…oadmin/workspaces fields

rds picks up its 'leaks' family row, which the parser had been dropping on a
reserved-word collision. Operations badge 6169 -> 6172.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both fields arrived in the SDK after this service was audited (types.go:52 and
:550 at the pinned v1.53.5) and were silently omitted from every response.

Unlike most of this sweep these are not caller-supplied — they are derived from
the org tree gopherstack already models, so leaving them unset would have been
the wrong answer. Getting the format wrong would be worse than omitting them
though, and the Go doc comments pin nothing ("The paths in the organization
where the account exists"), so the format comes from the AWS API Reference
example responses and the published regex, cited in buildPath's comment:
o-<org>/r-<root>/(ou-<id>/)*<ownID>/ — org, root, ancestor OUs top-down, the
resource's own id, trailing slash.

Paths is plural but Organizations is a strict single-parent tree — moving an
account between roots is an error, MoveAccount takes one source and one
destination, and this backend stores a single accountParent. It therefore
always returns exactly one path and never fabricates a second.

Populated on the seven operations that actually return these types, found by
searching for the types rather than trusting the gap note: DescribeAccount,
ListAccounts, ListAccountsForParent, DescribeOrganizationalUnit,
UpdateOrganizationalUnit, ListOrganizationalUnitsForParent and
CreateOrganizationalUnit. ListChildren and ListParents are excluded because
they return summary types that carry no path in real AWS.

The ancestor walk is bounded, so a cyclic or dangling parent chain cannot spin:
it returns no path at all rather than a partial or invented one. That state is
unreachable through the API and only constructible via a corrupted snapshot,
which is how the test builds it.

Nothing new is persisted. Both fields are json:"-" and computed at read time
from state that was already stored, so organizationsSnapshotVersion stays at 1.

Refs gopherstack-gt9o

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
NetworkType arrived after this service was audited and was dropped end to end —
absent from the inputs, never echoed, error not in the lookup table.

DBCluster.NetworkType (types.go:236) is settable on CreateDBCluster
(api_op_CreateDBCluster.go:171) and ModifyDBCluster (:136), so it is accepted,
stored and echoed. It defaults to IPV4 because the SDK documents that as the
default in as many words — "IPV4 – ( the default )" — not because a default
seemed reasonable.

DBInstance.NetworkType (types.go:764) has no input member on either
CreateDBInstance or ModifyDBInstance; the SDK says it is inherited from the DB
cluster, so that is where this takes it from rather than inventing an option
the API does not offer.

NetworkType is a bare *string with no entry in types/enums.go, so any value is
accepted. Restricting it to IPV4/DUAL would be stricter than the real API.

Two things are deliberately left inert, and both would have been easy to fake:

SupportedNetworkTypes on DBSubnetGroup (types.go:945) and
OrderableDBInstanceOption (types.go:1291) is modelled on the wire in its real
member-wrapped list shape but never populated. Subnets here are opaque ID
strings with no CIDR data, and the orderable-options catalog is static, so
there is no honest basis to say which network types are supported. A fabricated
capability list is worse than an absent one, and a test asserts it is genuinely
absent from the XML rather than present and empty.

NetworkTypeNotSupportedFault (errors.go:1417) is not added to the error lookup
table. Real Neptune raises it when a requested network type conflicts with the
subnet group's actual CIDR support — a condition this backend cannot detect.
Inventing a rejection so the error had something to raise would be the
more-restrictive-than-AWS bug class.

neptuneSnapshotVersion stays at 1; the new fields are additive omitempty.

Refs gopherstack-gt9o

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ates

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…berately never populated

The field arrived after this service was audited (types.go:803, :969, :6079 at
the pinned v1.73.4) and was missing from GetAutomationExecution,
DescribeAutomationExecutions and DescribeAutomationStepExecutions.

It is modelled and left permanently unset, which is the honest outcome rather
than a shortcut. Real SSM sets it when its engine detects a non-critical issue
mid-run; StepExecution's doc adds "Present only if the step status includes a
warning". There is no such status in the enum, so it is engine-detected, not a
modelled transition. This backend has nothing to detect: completeAutomationLocked
drives every step to Success unconditionally, and automationStatusFailed is
declared in store.go but never assigned anywhere — there is no failure, timeout,
retry or degraded path to report a warning from.

Inventing a warning string would put text in front of an operator that no real
condition produced. Same call as apigatewayv2's failOnWarnings on this branch,
which is validated but documented as inert because the emulator generates no
import warnings.

The test asserts the field is genuinely absent from the raw response body, not
merely empty when parsed — those are indistinguishable through the SDK, and
omitempty is the only thing separating them.

ssmSnapshotVersion stays at 1.

Refs gopherstack-gt9o

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…PC config

Both fields arrived after this service was audited and were read nowhere.
Connector.IpAddressType (types.go:720) is set by CreateConnector
(api_op_CreateConnector.go:86) and UpdateConnector (:80), and echoed on
DescribeConnector. WebAppVpcConfig (:2745) and UpdateWebAppVpcConfig (:2648)
carry their own, set through Create/UpdateWebApp's EndpointDetails.

Two absences here are real AWS behaviour and are deliberately preserved, with
tests pinning them so a later pass does not "fix" them into existence:

DescribedWebAppVpcConfig (types.go:1417) has no IpAddressType and no
deserializer case for one, so a client sets it and cannot read it back. The
describe output is untouched. This is the same asymmetry PARITY.md already
records for SecurityGroupIds.

ListedConnector (types.go:1897) carries only Arn, ConnectorId and Url, so
ListConnectors keeps omitting the field.

Neither enum is validated. Both are IPV4/DUALSTACK, but the sibling
Server.IPAddressType — the same enum shape — is threaded through servers.go
with no validation, while EndpointType, Domain and TLSSessionResumptionMode in
that same file do validate. Following the established local precedent for this
exact shape rather than inventing strictness AWS may not have.

The web-app value is stored despite never being echoed: it round-trips through
Snapshot/Restore and is readable from the backend struct, matching how
SecurityGroupIDs is already handled here.

transferSnapshotVersion stays at 1.

Refs gopherstack-gt9o

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous checkpoint described chore/parity-upgrade, which has since merged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…t add

git refuses any `git add` naming a path that matches an exclude pattern,
regardless of whether the path is already tracked. bd's auto-export hook runs
`git add .beads`, so it failed on every create/close, and resolving a merge
conflict in issues.jsonl needed -f.

Narrow the pattern to .beads/* with a negation for issues.jsonl. Directories
are pruned whole, so embeddeddolt/ (88M) and backup/ (53M) stay out.

Closes gopherstack-nejg
Closes gopherstack-nejg
Closes gopherstack-ky42
…count

The outpost path kept a maxInstancesPerRunInstancesRequest-sized capacity
hint, reserving ~16KB for a one-instance request. The non-outpost path at
store.go:956 already solved this: make([]*Instance, 0) with no hint and
//nolint:prealloc, keeping count out of the make() size so CodeQL alert 253
(go/uncontrolled-allocation-size) stays closed. Mirror that here.

Extract the ID-minting loop into newOutpostReservedInstanceIDs. The new test
asserts cap(ids) <= count*4, which fails against the old fixed-1000 code for
any count under 250.

Closes gopherstack-2vgi
…efix on the wire

Shape-only, deliberately never populated, following b4f91c2. Gopherstack has
no real dual-stack endpoint, and a fabricated dialable URL is worse than an
absent field.

Also correct the PARITY.md gap entry: GetHlsManifestConfiguration does not
exist in mediatailor v1.63.4 (48 ops, no such operation), and there is no
separate SessionInitializationEndpoint type carrying its own dual-stack prefix
- that field appears once, on PlaybackConfiguration, already covered by gt9o.

Closes gopherstack-ic73
…ations

All six take a required ResourceId member (workspaces v1.73.1, serializers.go
8368/8423/8442/8461/8480/8499). Gopherstack read DirectoryId, so every real
client's identifier was dropped and the call looked for a key no client sends.
The tests asserted DirectoryId too, so they enshrined the bug instead of
catching it.

Not a blanket rename: ModifyEndpointEncryptionMode really does take
DirectoryId, and ModifyClientProperties already read ResourceId. Both verified
and left alone.

Also wire ModifyCertificateBasedAuthProperties.PropertiesToDelete, which acts
on the same persisted ds.Properties map the set path already writes.

TestDirectoryModifyOps_RejectsLegacyDirectoryIdKey sends only the legacy key
against a registered directory and expects 404, so a revert fails the suite.

Closes gopherstack-7rq1
Witness Patrol added 30 commits August 14, 2026 07:43
…ed 200

The real key is Identifiers.DeleteClusterSnapshotMessage.N.SnapshotIdentifier.
The handler read the parent key directly and then fell back to a second form,
and neither is anything a real client sends - so every batch delete succeeded
loudly and deleted nothing. Three existing tests posted the fallback shape, so
tests and handler agreed on a request format AWS never produces. Third instance
of that pattern this campaign.

Three partner ops emitted ClusterIdentifier, which their real outputs do not
carry. A test asserted its presence by substring, entrenching the invention.

Two custom-domain ops dropped CustomDomainCertExpiryTime entirely - no field
existed for it. Generated the same fabricated-but-consistent way Redshift
Serverless already handles its own cert expiry, rather than inventing a second
convention.

Two snapshot-schedule ops dropped Tags the backend already accepts and stores.
NextInvocations left unfixed and documented: its cron grammar differs from the
one this service already parses for scheduled actions, and adding a second
parser is disproportionate to the gap.

Empty-envelope class ABSENT in redshift - nine genuinely-void deletes confirmed
void against the real SDK rather than 'fixed'.

Coverage stated as found: redshift classic exhaustive at ~120 ops, serverless
sampled, and the other four services sampled at 10-12 mutating ops each on top
of their existing same-week field-diff passes.

Refs gopherstack-7185
…client sends

Query-protocol list members are wrapped in a per-type element name, not the
generic member. neptune read member everywhere: SubnetIds.member.N where the
real key is SubnetIds.SubnetIdentifier.N, and the same for
VpcSecurityGroupIds.VpcSecurityGroupId.N and
AvailabilityZones.AvailabilityZone.N.

Worst is the shared filter parser. It read Filters.member.N.Name and
Values.member.1 where the real serializer sends Filters.Filter.N.Name and
Values.Value.1, so EVERY filter on DescribeDBClusters, DescribeDBInstances and
DescribePendingMaintenanceActions was silently ignored - unfiltered results
returned as though the filter had applied.

Roughly sixteen tests posted the wrong keys. One asserted that subnet ids it had
never successfully sent appeared in the response, which is a fabrication
ratified twice over.

Found response-side while fixing the third: xmlDBCluster had no
AvailabilityZones field at all.

THE SWEEP DIRECTION WORKED, and its negative half is worth as much. Checking
tests as claims against the SDK, the agent first asked which services CAN have
this bug - then proved elb, iam, sts, sns and ses structurally cannot, because
every list in those five serializers uses the generic member wrapper with no
custom overrides. Four services were already fixed by earlier passes. That
narrowed twelve candidates to two.

Closes gopherstack-rip4
…ur ops unreachable

The generated-template family is the worst and was found incidentally while
writing a regression test for something else. Update, Delete, Describe and Get
all read GeneratedTemplateId where the real wire key is GeneratedTemplateName,
so all four were unreachable by any real client - and an existing test masked
it by accepting HTTP 400 as a pass.

Nine cloudformation ops dropped identifiers their real outputs carry, including
OperationId on CreateStack, UpdateStack and RollbackStack. UpdateStackSet and
ImportStacksToStackSet were the notable ones: the backend already computed the
operation id and threw it away.

route53 invented two elements that are not on the wire at all. elbv2 dropped
three members from its subnet and security-group modify ops. apigateway's
shared RestAPI model was missing version, securityPolicy and warnings across
four ops.

TWO CORRECTIONS TO MY OWN DISPATCH. apigateway is REST-JSON and
case-SENSITIVE, not query/XML as I told the agent - it checked rather than
trusting me. And the scripted diff had a 65 percent false-positive rate here,
mis-attributing anonymous struct literals to unrelated named types; every flag
was hand-read and several confirmed clean rather than 'fixed'. The method
scales, but only with that discipline.

Coverage stated honestly: cloudformation, route53 and elbv2 exhaustive;
apigateway SAMPLED - all 22 deletes clean, ~20 creates diffed, ~45 updates not
reached. Named as the weakest-covered service.

Six backend signatures widened to return values that were being discarded; full
build verified across every caller.

Also reverted fieldalignment's collateral reordering of two elbv2 test files
that carried deliberate ordering with justifying nolints.

Refs gopherstack-7185
…thing

Three were not merely lax - the request never reached the operation at all, and
the accepted failure code hid it permanently.

Two organizations tests posted a nil body, so json.Unmarshal on zero bytes
always errored and ListCreateAccountStatus and ListHandshakesFor* were ALWAYS
returning 400. A ram test called a route that does not exist, with an ARN
carrying both a typo and a nonexistent permission name, so it always got
'unknown action'. All three passed for as long as they existed.

The autoscaling one is the clearest artefact: Code == StatusOK || Code !=
StatusInternalServerError accepts every status except literally 500, and the
response body it captured was discarded into a blank identifier.

Also an iam test accepting 200, 404 or 500, an ssoadmin update never checking
the name changed, and - directly below the cloudformation test that motivated
this class - its sibling carrying the identical unfixed pattern.

All nine now assert what a caller observes: the id returned matches the one
sent, the created record appears in the list, the updated name reads back.

THE HONEST RESULT: no application-logic bug sat beneath any of them. Every
backend op worked once driven properly. So the yield here is different from the
wire sweeps - what these tests concealed was their own emptiness, plus three
requests that never arrived.

Ten further candidates examined and deliberately left: 200-or-201 on quicksight
creates and 200-or-202 on pinpoint async ops are legitimate status variance
with real content assertions following, and one honestly-named Smoke test is
covered properly elsewhere.

Refs gopherstack-mslf
…ds silently no-op'd

TestInvokeAuthorizer shares its URL with Get, Update and Delete and is
distinguished only by method. The router's authorizer route handled GET, PATCH
and DELETE but not POST, so every real client call 404'd.

The PATCH bet paid off, though not where I guessed. patch.go itself is mature
and handles the real key set correctly - it has dedicated resolvers per
resource family and real-client tests already. What it cannot catch is a patch
landing on a field the target struct does not have, which silently does
nothing and returns the unmodified resource. Two instances: /securityPolicy on
UpdateRestApi, already named-but-unfixed in PARITY.md since 11 August, and
/authType on the authorizer ops - that one missing in TWO layers, the exported
input types AND hand-duplicated local decode structs in the handler.

Re-swept every other Update op for that same two-layer shape and found none;
the rest embed or unmarshal straight into their exported input.

GetUsage and UpdateUsage emitted items where the deserializer reads values, so
a real client's Items was always empty - and a test asserted the wrong key.

TestInvokeAuthorizer also emitted authorization as an integer where the real
type is a map of string to string list, which HARD-ERRORS a real client rather
than failing quietly, plus an invented context key.

Deployment.apiSummary and Stage.webAclArn added; TestInvokeMethod gained
multiValueHeaders in both directions.

Five findings reported and deliberately not fixed, including two invented wire
keys whose removal needs a persistence-table migration that is not worth the
risk for keys real clients ignore.

Refs gopherstack-7185
…us five empty tests

Three sqs filter-policy tests were COMPLETELY EMPTY - no invocation, no
assertion - while promising coverage of the seven-operator matcher that governs
SNS-to-SQS delivery. The purest form of this class: a test that cannot fail.
Replaced with sixteen table cases driving real Publish through ReceiveMessage.

The one real wire bug came from the bare-NotNil pattern:
DescribeEngineDefaultParameters and its cluster sibling never echoed
DBParameterGroupFamily, which the real EngineDefaults declares. Both tests
asserted only that a response existed.

Three more tests tightened with no bug beneath: a revoke that never checked the
CIDR was removed, a describe that never checked the returned protection, and an
update that never checked the updated fields.

VOLUMES, because they bound the class rather than close it: the NotNil pattern
had ~179 hits, machine-triaged to 48 where it is the last statement, ~35 read,
5 real. The behaviour-promising-name pattern had ~1878 test functions, triaged
to 173 lacking content assertions, ~25 read, 1 real - and most triage flags
were false positives from the detector not counting Len, Empty, True and False
as content checks. Both remain far from exhausted, and the low hit rate is
itself the useful signal.

Reported and not fixed: two independent filter-policy engines exist, and the
sqs-side one is dead for the exclusion path because SNS already prunes
non-matching subscribers before it runs.

Refs gopherstack-mslf
The real SDK sends DELETE on a six-segment path ending /cache/data. The router
checked for five segments ending /cache, so it never matched and fell through
to the unknown-operation sentinel. A pre-existing test hand-built the same
five-segment path and thus masked it - the same antipattern this campaign has
now found five times.

The route table that guards this service grew from 41 ops to all 124, so the
gap that hid FlushStageCache is closed structurally rather than by one fix.

s3's ListDirectoryBuckets is NOT fixable and is now documented as such. The
router keys on a list-type=directory query parameter that no real client ever
sends: AWS distinguishes that op from ListBuckets purely by hostname,
s3express-control versus s3, and gopherstack has one endpoint. So every real
call falls through to ListBuckets and returns the wrong bucket set with a 200.
Swapping one fabricated discriminator for another would only move the lie, so
it carries a landmine comment and a PARITY gap entry instead.

Four of the six services in scope were already covered by earlier route sweeps
driving both ExtractOperation and Handler; their suites were re-run to confirm
no regression rather than re-derived. The new ground was s3 and apigateway's
restapis subtree, which a prior note had explicitly flagged as needing a method
of their own.

Two ops confirmed correct and recorded so they are not re-suspected: s3
disambiguates CopyObject and UploadPartCopy by the copy-source HEADER rather
than by method or query, which reads like a bug and is not.

Closes gopherstack-0bq8
…assing

ecs 77, ssm 152, kms 54, stepfunctions 37, secretsmanager 23 - every op in each
pinned SDK, cross-checked against GetSupportedOperations before a test was
written. Both ExtractOperation and Handler are driven, matching the convention
the other 25 tables follow, because the first is an observability hook and only
the second is the dispatch contract.

THE USEFUL RESULT IS THE BOUND, not the zero bugs. These five dispatch by
X-Amz-Target, never by path - every op posts to / with the same method, and the
only per-op signal is the target header. So there is no path template to get
wrong, and this whole protocol family is structurally immune to the class that
made apigateway's FlushStageCache unreachable.

What remains possible is narrower and worth guarding: a dispatch key that does
not exactly match the real op string. These tables catch that by driving the
REAL SDK's target rather than gopherstack's own name for the op, and
case-sensitivity makes it a live risk. A static diff of implemented keys against
serializer-derived names found no mismatch in either direction, then the tests
confirmed it by actually dispatching.

That bound matters for scope: roughly 130 of 161 services are JSON-family, so
the unreachable-op class is concentrated in the REST and query minority rather
than spread across the repo.

Also reconfirmed independently: stepfunctions omits DescribeStateMachineVersion
because the SDK has no such op either.

Refs gopherstack-n1mb
s3 112, iam 176, dynamodb 58, sns 42, sqs 23 - every op in each pinned SDK,
built from httpbinding.SplitURI in the serializers rather than from the
handlers' own routing, so the table cannot inherit the router's mistakes.

s3 needed more than the static template. Several ops are distinguished by
REQUIRED DYNAMIC members the template cannot show - UploadId and PartNumber for
UploadPart, the copy-source header for CopyObject and UploadPartCopy, an
annotation name, four config ids. Without reading each leaf handler's
HttpBindings the synthetic requests would not match what a client really sends,
and the table would have passed vacuously.

Two per-service verification quirks worth recording. s3 computes
ExtractOperation from a field each leaf handler tags itself with, so one read
after Handler proves routing AND self-identification together - there is no
separate unmatched-route sentinel. And iam REUSES its InvalidAction error for
ordinary bad input, so checking that code alone produced eleven false
positives; the table matches the exact dispatch-miss phrase instead.

The table was proven capable of failing rather than assumed: breaking
PutBucketAcl's query check made it fall through to CreateBucket and the test
caught it.

ListDirectoryBuckets is skipped with its reason rather than asserted, since
AWS distinguishes it by hostname alone. No other structurally unreachable op
found in these five.

No bugs. With the 343 target-header ops tabled earlier, 754 operations across
ten services now carry a standing route assertion.

Refs gopherstack-n1mb
securityhub 116, xray 38, glacier 33, efs 31, each matching its pinned SDK's
real op count exactly. All four drive Handler as well as ExtractOperation.

Proven capable of failing rather than assumed: mistyping one securityhub path
literal made that op fall through to Unknown and the table caught it.

I told the agent glacier is REST-XML. It is REST-JSON 1 - the agent read the
serializers rather than trusting me, and _PROTOCOLS.md already said so on a row
that was hand-spot-checked. That is the third protocol claim I have gotten
wrong from memory while holding a table built precisely to stop that.

Two sentinel subtleties worth recording, both of the iam InvalidAction kind.
xray's legitimate not-found errors CONTAIN the string 'not found' as JSON, so a
substring check would have false-positived; the table compares against the bare
plain-text sentinel exactly. The other three use a phrase confirmed absent from
non-test code.

Systematic check for the s3 vacuity trap - a shared method and path
discriminated by a dynamic member the template cannot show - found exactly one
collision in all 218 ops: glacier's AddTags and RemoveTags, both POST on the
same path, separated only by an operation query baked into each op's own
template. Already handled correctly. So no additional dynamic-member
verification was needed anywhere else in this batch, which is a real bound
rather than an omission.

Glacier's unusual shapes held: the account-id segment including AWS's dash
placeholder is opaque to the router, and its multipart tree discriminates
cleanly by method on one identical path.

Refs gopherstack-n1mb
…vert - 319 ops

112, 100, 73 and 34, each matching its pinned SDK's real op count. All four
drive Handler as well as ExtractOperation.

Zero method-and-path collisions across all 319 ops, checked systematically
rather than assumed. So the s3 vacuity trap - a shared route separated only by
a dynamic member the static template cannot show - does not arise here at all.
With the previous batch's one collision in 218 ops, that trap now looks rare
enough to be a named exception rather than a general hazard.

Sentinels were verified before being trusted, which two earlier services proved
necessary. sesv2 turned out to have TWO distinct dispatch-miss modes - an
unmatched path and a recognised op with no dispatcher case - and both are
driven. Each phrase was grepped to confirm it appears exactly once outside
tests.

Proven able to fail: removing one case from mediaconvert's read-only dispatch
made that op fall through to the mutating table's default, and the test caught
it with the exact NotFoundException body.

mediaconvert's table excludes UpdateJob deliberately - it is not a real SDK op,
and the handler already documented that. Three unusual-but-correct routes
recorded so they are not re-suspected: a DELETE-based CancelJob, a PUT on the
tags path, and a prefix ordering that puts jobsQueries before jobs.

Protocols taken from _PROTOCOLS.md rather than from my memory this time.

Refs gopherstack-n1mb
…ication bugs

amplify 37, bedrockagent 75, elasticsearch 51, ram 35, grafana 25, mwaa 12,
each matching its pinned SDK's real op count.

The three bugs are all observability-only, and that distinction is the
interesting part: runtime dispatch was correct in every case, so a table
driving only Handler would have passed. PrepareAgent had no classification case
for its shape; ValidateFlowDefinition was misclassified as PrepareFlow because
both are a single segment plus POST; and an elasticsearch inbound-connection
case sat in the OUTBOUND classifier, dead because the inbound prefix always
routes first. All three would have mislabelled metrics and logs indefinitely.

That is the inverse of the reason we drive Handler as well as ExtractOperation
- one catches unreachable ops, the other catches mislabelled ones, and this
batch needed the second.

The sentinel work keeps paying. bedrockagent has EIGHTEEN distinct miss
branches behind one exception type. amplify has two miss modes, and its
not-found sentinel collides on substring with real domain errors, so the table
matches the exact JSON fragment rather than a bare substring - the same trap
xray and iam set. mwaa also has two.

Zero method-and-path collisions across all 235 ops, so the vacuity trap now
stands at one in 772.

ram's internal ListTagsForResource is excluded rather than tabled: its own
comment says it is not a real SDK op, verified against botocore's service
definition.

Proven able to fail on already-clean code - removing grafana's GET case made
DescribeWorkspace classify as empty.

Refs gopherstack-n1mb
…ration, plus 732 ops tabled

The iot findings are the worst of the campaign. DetachThingPrincipal did not
404 - it dispatched to DeleteThing. EnableTopicRule and DisableTopicRule
dispatched to CreateTopicRule. A caller detaching a principal destroyed the
thing instead. Also an AttachPrincipalPolicy and DetachPolicy name mix-up
mapping the wrong op to a real path, and a CA-certificate family on a path that
does not exist.

quicksight had four unreachable ops: two asset-bundle ops missing a literal
path suffix, and two that accept only PUT where the real method is POST.

bedrock's PrepareAgent was unreachable, and its in-package agents sub-API
recognised only 10 of its 75 ops. Nine of those share a collection path with a
PUT sibling and are distinguished by method - real AWS uses POST for the List
where gopherstack conflated POST with Create.

About thirty more bedrock ops dispatched correctly but classified as Unknown,
which is the observability half this table catches and Handler alone does not.

Tabled: bedrock 108, its agents sub-API 75, quicksight 277, iot 272. quicksight
and iot are the two services flagged early in this campaign as sitting in the
gap between sweeps - the gap is now closed with a standing guard.

iot's existing whitebox test had already TRACKED 24 of these as known-unfixed
gaps. They are now closed and that list updated. A test that records known
breakage without failing is a coverage metric agreeing with itself.

Proven able to fail per service, and the iot proof is the point: reverting the
DetachThingPrincipal fix made it mis-route to DeleteThing rather than 404.

Refs gopherstack-n1mb
… marks optional

The real shape requires only the provider role ARN and URI. A client omitting
Name - as the service's own test did - got a spurious 400.

It survived because the test called t.Skip with the reason 'recommender
creation returned 400, skipping rest of test'. So the failure was observed,
described accurately, and converted into a pass, leaving the whole CRUD path
unexercised. A sibling table case encoded the same wrong requirement as a
passing assertion named rejects_empty_name.

Both are corrected: the skip is now a require, and the table asserts Name is
OPTIONAL while checking the two fields that genuinely are required.

THE SWEEP'S WIDER RESULT IS THAT THIS CLASS IS RARE HERE. Four distinct list
mechanisms and all ten t.Skip sites in the repo were checked, and three of the
four mechanisms are legitimate ratchets rather than graveyards.

The sdk_completeness pattern across ~160 files tracks entirely-unimplemented
ops and is backed by a gate that fails on any stale entry; redshift's history
shows dozens removed as ops landed. sesv2's three-entry gap list cites SDK
lines and is guarded by a hard bucket-count assertion. And iot's list - the
instance that motivated the issue - is already empty, closed by the pass that
found it.

So the pathology was real and is not endemic. That bound is the useful half.

Refs gopherstack-2y20
batch 45, s3control 97, accessanalyzer 39, resourcegroups 23, scheduler 12,
pipes 10. Every count matched its service's own GetSupportedOperations exactly,
so no op was undercounted.

s3control was flagged as the likeliest place for the vacuity trap - REST-XML
with account-id routing - and came back with zero collisions across 97 ops.
That keeps the campaign-wide count at one collision in roughly a thousand ops,
which settles it as a named exception rather than a hazard to design around.

The sentinel check earned its place again, on scheduler. Its real not-found
errors legitimately contain the string 'not found', which collides with the
routing-miss sentinel of the same text - a bare substring assertion would have
failed on working code. Resolved by matching the body exactly, since misses
write bare text and domain errors write JSON. Fifth service where that check
prevented a phantom bug.

Each table proven able to fail by mis-wiring one route and watching it dispatch
to a sibling - CancelJob to TerminateJob, GetSchedule to DeleteSchedule,
StartPipe to StopPipe, and three more.

Two structural notes recorded rather than 'fixed': resourcegroups' handler
comment claims AWS uses DELETE for Untag where the pinned SDK sends PATCH -
routing is correct and only the comment is stale - and s3control's synchronous
MRAP delete is unreachable by design, with the async route already tabled.

Refs gopherstack-n1mb
mq 25, appmesh 38, serverlessrepo 14, mediapackage 19, emrserverless 22,
networkmonitor 12. No bugs, no exclusions, no stale-comment cases.

Three method conventions confirmed from the serializer rather than assumed, all
of which REST intuition would get wrong: appmesh uses PUT for every Create,
serverlessrepo uses PATCH for UpdateApplication, and mq carries a versioned v1
prefix while mediapackage's paths are bare.

The sentinel check saved a sixth service. appmesh's legitimate not-found errors
are all qualified - 'mesh not found', 'virtual router not found' - so every one
contains the bare miss sentinel as a substring. Exact body matching instead,
verified against all seven emission sites.

One scope limit worth recording rather than glossing: mediapackage's bare paths
are shared with iotanalytics, mediatailor and fis at the same prefix, and are
disambiguated a layer up in RouteMatcher by SigV4 service name. Every route
table in this campaign drives ExtractOperation and Handler directly, so none of
them covers that layer. That is a real gap in what these guards assert, not a
property of mediapackage.

Each table proven able to fail by mis-wiring a route and watching it dispatch
to a sibling - appmesh's swap moved five subtests at once.

Refs gopherstack-n1mb
detective 29, fis 26, iotanalytics 34, managedblockchain 27, resiliencehub 63,
rolesanywhere 30. Every route matched its serializer on first pass.

The sentinel check caught two more real substring collisions. iotanalytics'
domain errors read 'channel not found' and managedblockchain's read
'ResourceNotFoundException: resource not found' - both contain their service's
bare miss sentinel, so a substring assertion would have failed on working code.
Both resolved by decoding the field and comparing exactly. Eight services now
where verifying the sentinel before trusting it was load-bearing.

Two useful negatives from the same check: rolesanywhere cannot collide because
its domain errors carry no message text at all, and fis cannot because its
not-found sentinels are single CamelCase tokens. Also found dead code in fis -
a second unknown-operation branch unreachable via HTTP because the path parser
only ever returns a known constant or empty, and empty short-circuits earlier.

The failure proofs were chosen to mirror real bugs rather than to be
convenient. iotanalytics' DeleteChannel was wired to DescribeChannel and
rolesanywhere's GetTrustAnchor to DeleteTrustAnchor - a destructive op shadowed
by a read, and a read turned destructive. Those are the shapes that made iot's
DetachThingPrincipal delete things, so proving the guard catches them is worth
more than proving it catches a 404.

Refs gopherstack-n1mb
dlm 8, account 16, mediastoredata 5, apigatewaymanagementapi 3, cloudfront
keyvaluestore 6. All clean on first pass.

I suspected cloudfrontkeyvaluestore had s3's ListDirectoryBuckets problem - an
op distinguishable only by hostname and therefore unreachable in a
single-endpoint emulator. It does not. The agent read the endpoint ruleset
rather than accepting the premise: AWS derives a per-ACCOUNT virtual host from
the KVS ARN, not per-store, and the full path carrying that ARN as a URI label
is generated independently of the host and sent unchanged either way. So every
op stays distinguishable by path. Corrected finding, not the assumed one.

apigatewaymanagementapi's live-handle oddity turned out to be a persistence
concern with no bearing on routing; its three ops share one path and separate
by method alone. Its gopherstack-only diagnostic endpoints are excluded rather
than tabled.

A new kind of clean bound from the sentinel check, in two services: the
dispatch-miss branch is structurally unreachable by any legitimate request.
mediastoredata's fires only for methods outside the four the API uses, and
apigatewaymanagementapi's only for paths lacking a prefix RouteMatcher already
requires. Previous bounds were about sentinels that could not collide; these
cannot fire at all.

Both failure proofs mirror the destructive shape deliberately - GetLifecyclePolicy
wired to Delete, GetKey wired to DeleteKey.

Refs gopherstack-n1mb
…lus 43 ops tabled

modelPathOperation matched on suffix alone, so InvokeGuardrailChecks - whose
real path is POST /guardrail-checks/invoke - classified as InvokeModel because
both end in /invoke. Dispatch was always correct, since Handler prefix-matches
rather than suffix-matches, so this was purely observability. Fourth
classification-only bug this campaign, and all four were invisible to a table
driving Handler alone.

iotdataplane is a ninth sentinel-collision service, and the sharpest yet: its
miss text 'not found' is a substring of three domain errors that reach the wire
verbatim - shadow, retained message and connection not found. A substring
assertion would have failed on legitimate 404s raised by the table's OWN test
cases.

Ops tabled: iotdataplane 11, bedrockruntime 11, polly 10, rdsdata 6,
sagemakerruntime 3, appconfigdata 2. iotdataplane's supported-operations list
claims 14 because three are gopherstack-only admin extensions with no AWS wire
op; excluded rather than tabled, as already documented in code.

A clean negative against my own expectation: I warned these data-plane services
would need header or content-type disambiguation because they stream audio and
model responses. None did. Method plus static path was sufficient for all six,
and rdsdata has no path parameters at all.

Failure proof chosen to hit real dispatch rather than labelling - swapping
iotdataplane's GET and DELETE classification would have made a real GetConnection
call delete the connection.

Refs gopherstack-n1mb
…s - 1244 ops

ec2 785, rds 164, redshift classic 145, cloudformation 90, redshift serverless
60. ec2's real op count came in at 785 against my ~500 estimate; the agent
tabled it in full rather than partially, since extraction and generation were
mechanical.

The static two-way diff before any test ran is what earned its place. It found
a DEAD dispatch key in rds - GetPerformanceInsightsMetrics matches no real RDS
Action at all - and five redshift-serverless ops with no dispatch entry, the
Reservations family the SDK gained and this backend never implemented. Both
excluded rather than tabled, so the tables assert what exists.

Neither would have surfaced from testing alone: a dead key is never exercised,
and an unimplemented op has nothing to drive.

Sentinels verified against the iam pattern and all five are clean - none of
these services reuses its dispatch-miss error for ordinary bad input, unlike
iam's InvalidAction. Recording that as a bound rather than a non-event.

Failure proof chosen for shape: mis-keying DeleteDBInstance to DeleteDBInstances
makes a destructive op unreachable, and the table caught it.

Refs gopherstack-n1mb
…cloudwatchlogs - 949 ops

sagemaker 403, glue 299, cognitoidp 129, cloudwatchlogs 118. glue turned out to
be 299 rather than the ~200 I estimated - real count, not the guess.

The agent went past the brief in the way that mattered: it diffed the ACTUAL
dispatch map rather than only the reported GetSupportedOperations list. That
found cognitoidp wiring AdminSetUserMFASetting, which is not a real SDK op name
- the real one is AdminSetUserMFAPreference, separately and correctly wired.
Dead cruft, unreachable by any client, and invisible to a diff of the reported
list because that list already omits it. Recorded, not 'fixed'.

It also declined to inherit the sentinel convention. Three of the four use
UnknownOperationException; sagemaker does not - its text is 'unknown action',
and the assumed literal appears nowhere in its non-test code. Each was grepped
to a single production call site.

And it distinguished a real risk from a false one: sagemaker dispatches by
switch rather than map, so phantom keys hide differently. Grepping every case
label turned up one hit that was a field-decoding switch, correctly dismissed.

Failure proof: renaming glue's DeleteTable binding to GetTable made the
destructive op unreachable while GetTable kept passing - now silently bound to
the delete handler. Exactly the shape that made DetachThingPrincipal delete
things.

Refs gopherstack-n1mb
…lus 323 ops tabled

DeleteCacheCluster and DeleteReplicationGroup indexed Data[0] straight after a
Describe that had succeeded with an empty slice. Query semantics mean an empty
id describes ALL, so on a fresh backend both panicked rather than returning
NotFound. Found by driving the table live, not by reading - each function
already had the correct fault on a sibling path.

eventbridge needed the most judgement and got it. Its supported-operations list
reports 79 where the service has 57: twenty-two belong to the separate Pipes
and Schemas services, both REST-JSON in their own SDKs, hosted in this
directory behind a fabricated target convention no real client can send. All
excluded. Four more keys are dispatchable but already documented in-source as
dead. The agent also used the REAL AWSEvents target prefix rather than the
existing tests' AmazonEventBridge one, which only passes because
ExtractOperation never checks the prefix - so those tests would accept a target
AWS does not send.

memorydb carries one excess key, ExportSnapshot, verified against botocore as
not a real op and already documented. Excluded rather than fixed.

autoscaling and directoryservice diffed exact in both directions with nothing
dead or missing.

Failure proofs on destructive ops in two services - mis-keying
DeleteAutoScalingGroup and DeleteDirectory.

Refs gopherstack-n1mb
… - 423 ops

dms 119, awsconfig 102, workmail 92, transfer 71, kinesis 39. All five diffed
clean in both directions - no dead key, no gap - unlike rds and
redshift-serverless in earlier passes.

Two services would have produced false positives from an inherited sentinel.
workmail's dispatch-miss maps to InvalidParameterException, THE SAME WIRE TYPE
its ordinary validation errors use, and transfer's maps to
InvalidRequestException, likewise shared. Asserting on the wire type would have
been the iam trap exactly. Both assert on message text instead, each grepped to
a single production call site. awsconfig's miss is not wire-typed at all.

The dms diff nearly produced a false GAP for the opposite reason: four ops are
dispatched by literal string keys where every other entry uses an opXxx
constant, so an identifier-only grep missed them and reported four ops
unimplemented. Re-extracting for both key styles resolved it to 119 of 119.
That is the same shape of risk that hid cognitoidp's phantom key, in reverse -
and kinesis has the same exposure, its supported-operations list being
hand-maintained separately from its real map.

kinesis was flagged for disguised stubs, so all 38 dispatched ops were checked
for hollow handlers. None found. SubscribeToShard is covered separately because
it bypasses JSON dispatch for the binary event-stream protocol.

Failure proof on a destructive op, and the report notes the limit honestly: the
mis-wired sibling kept passing, since this table proves dispatch RESOLVES, not
that it resolves correctly.

Refs gopherstack-n1mb
workspaces 91, ssoadmin 79, waf 77, organizations 63, datasync 53, elbv2 51.
Every one diffed to exact real-op parity in both directions - nothing dead,
nothing missing, nothing excluded.

The extraction trap fired again and was caught. organizations initially showed
a false gap of one, because dispatchRoot tests ListRoots with a bare if where
every sibling helper uses a switch. Same shape as dms's four literal-keyed ops
last pass. Two services, two passes, two different idioms - so extracting for
one style and trusting the count is now demonstrably unsafe.

Target prefixes confirmed unguessable again: ssoadmin's is SWBExternalService,
datasync's is FmrsService, waf's is AWSWAF_20150824. None derivable from the
service name.

Sentinel handling split cleanly. Four services wire-code their dispatch miss
uniquely and can be asserted on type; datasync and workspaces share their miss
type with real validation and internal errors, so those assert on message text.
That is now eleven services where checking rather than inheriting mattered.

elbv2 had no prior table - its existing audit test covers draining, DNS format
and pagination, not dispatch - so this is additive rather than duplicate.

Failure proofs on a destructive op in each of the six. For elbv2 the agent also
reproduced the documented LIMIT: shadowing DeleteLoadBalancer with
DeleteTargetGroup's handler still passes, because the table proves dispatch
resolves, not that it resolves correctly.

Refs gopherstack-n1mb
personalize 71, athena 70, emr 65, codebuild 59, ecr 58, swf 39. All diffed
exact in both directions.

The sentinel check was load-bearing in four of six, and athena is the clearest
case yet: its dispatch-miss shares a wire type with FOUR other errors -
not-found, already-exists, protected and validation are all literally the same
constructed error. Asserting on type there would have passed against a handler
that had stopped dispatching entirely. codebuild and personalize share the same
shape; swf returns its miss with no type field at all.

swf's prefix is SimpleWorkflowService with no version suffix, unlike every
other older service tabled so far - read rather than assumed, as instructed.

A real finding recorded rather than fixed: personalize dispatches its two
Runtime ops through the control-plane target-header mechanism under a
FABRICATED AmazonPersonalizeRuntime prefix. The real personalizeruntime SDK is
REST-JSON and sends no target header at all - it POSTs to /recommendations
directly. So a real client's request cannot reach that handler. Stronger than
the eventbridge case, where the fabricated prefix was merely unvalidated rather
than contradicted by the protocol. The package's own tests drive the same
fabricated header, so they pass without touching the real wire protocol.

emr's ListTagsForResource dispatch key excluded - no such real EMR op, already
documented as scaffolding.

Failure proofs on a destructive op in each of the six.

Refs gopherstack-n1mb
rekognition 75, ses 71, wafv2 59, elasticbeanstalk 47, firehose 12, sts 11.
Both diffs exact in both directions, no dead keys, nothing excluded.

THE FAILURE PROOF CAUGHT A BUG IN THE PROOF ITSELF. rekognition's first
assertion used %q formatting, which produces unescaped quotes that never appear
in a JSON body where quotes are backslash-escaped. So the corrupted-key test
PASSED when it should have failed - a table that could not fail, which is the
exact pathology this campaign has spent days finding in other people's tests.
Caught only because running the proof is required rather than trusting the
assertion. Fixed and re-verified fail, revert, pass.

The extraction trap appeared in a THIRD idiom. ses dispatches through an
eight-deep chain of switches, each falling through its own default into the
next helper. Stopping at the first default would have undercounted; extracting
every case in every link landed exactly on 71. After dms's literal string keys
and organizations' bare if, that is three distinct idioms in three passes.

rekognition's sentinel is the catch-all default of handleError rather than a
dispatch-specific type, so asserting on type would have been structurally
unsafe even though nothing leaks into it today. Message text instead, following
athena.

Two useful structural notes. elasticbeanstalk's dispatch-miss and its two
sibling errors all wrap the SAME underlying error, and separation depends on
errors.Is matching each package var's own pointer identity - verified rather
than assumed. And rekognition's supported-operations list is built by ranging
over the dispatch map, so it cannot diverge from it, collapsing what is
normally two independent diffs into one.

Refs gopherstack-n1mb
codecommit 79, codedeploy 47, codepipeline 44, verifiedpermissions 34,
kinesisanalyticsv2 33, textract 25. Three lists extracted per service - real
SDK targets, the reported op list, and the actual dispatch map - and all three
matched byte for byte in every direction.

All six build their reported list as a hand-maintained literal rather than by
ranging over the dispatch map, so unlike rekognition the two diffs here are
genuinely independent checks. Worth stating explicitly, since a collapsed pair
looks identical in a report.

The codedeploy failure proof did double duty. Mis-wiring DeleteApplication
confirmed the table catches it AND confirmed that asserting on wire type alone
would NOT have, because the mis-wired response still carried
InvalidRequestException. That is the sentinel trap demonstrated rather than
argued.

Sentinels split three ways: two services wire-code their miss uniquely,
three share it with ordinary validation errors, and kinesisanalyticsv2 has two
separate miss paths emitting the same type outside the shared error handler.
codecommit is the sharpest - its miss has no table entry at all and falls
through to the error handler's loop default.

Two prefix oddities: textract's is bare Textract with no version suffix, and
kinesisanalyticsv2 reuses v1's KinesisAnalytics_20180523 rather than carrying
its own. verifiedpermissions is the only JSON-RPC 1.0 of the six.

No extraction trap in this batch - all six use a single flat map, none of the
three idioms found in earlier passes.

Refs gopherstack-n1mb
lightsail 161, route53resolver 72, neptune 70, docdb 55, shield 36, elb 29.

Six services, five different dispatch idioms: neptune chains seven switches,
docdb six, shield tries four helpers returning ok-flags, elb and lightsail use
flat maps. The extraction trap is not an occasional hazard - it is the norm,
and the only safe method is re-extracting per service rather than reusing the
previous pass's script.

route53resolver builds its reported op list by ranging over the same map
Handler uses, so the two diffs collapse into one self-referential check. The
agent re-derived 72 independently from all thirteen builder functions rather
than trusting it. lightsail is the opposite - a hand-written 161-entry literal
genuinely independent of its sixteen-function merge, so two real diffs.

Three of six could not safely assert on the wire type. shield's dispatch-miss
shares InvalidParameterException with two other errors, and the code says so in
its own comment; lightsail's shares InvalidInputException with validation
errors; route53resolver's branch emits NO type field at all. Message text in
all three, and the failure proofs then confirmed each prediction empirically by
printing the actual body.

shield's __SimulateAttack is a gopherstack-internal test hook with no real
target - excluded rather than tabled.

route53resolver's prefix carries no version suffix, like textract and swf.
That is now three of the last eighteen, so the version-stamped form is a
convention rather than a rule.

Refs gopherstack-n1mb
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