parity: per-service AWS audit of all 163 services - #2452
Open
agbishop wants to merge 741 commits into
Open
Conversation
|
Important Review skippedToo many files! This PR contains 2139 files, which is 2039 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2139)
You can disable this status message by setting the |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
UpdateResource's RFC 6902 engine covered add, replace and remove. move, copy and test were parsed and then silently skipped, so a patch containing them returned 200 with the document unchanged -- the client had no way to tell its patch had not been applied. All three now work, per RFC 6902: - move (4.4): remove at From, add the value at Path. Rejects From being a proper prefix of Path, since "a location cannot be moved into one of its children". - copy (4.5): add a deep copy of From's value at Path, leaving From intact. - test (4.6): succeeds only on JSON structural equality -- object member order insignificant, array order significant, 1 and 1.0 equal (both decode to float64 and re-marshal identically). Patches are atomic: ops apply sequentially against the document as mutated by prior ops, and any failure returns the ORIGINAL document string alongside the error, so UpdateResource assigns r.Properties only after the whole patch succeeds. add/replace/remove keep their pre-existing best-effort behavior -- an unresolvable Path is still a silent no-op for those three, never an error. A failed test (and an unresolvable move/copy From) maps to ErrValidation -> InvalidRequestException. The SDK declares no TestOperationFailedException and no ValidationException for cloudcontrol; InvalidRequestException's own doc comment reads "The resource handler has returned that invalid input from the user has generated a generic exception" (cloudcontrol@v1.32.4 types/errors.go), and it is already this file's mapping for every other malformed-request condition. Every *Fault-suffixed error describes a downstream handler failure during provisioning, not a client patch rejected before any handler runs. High confidence on internal consistency, moderate on exact real-AWS wire behavior -- the SDK has no dedicated code either way. Regression tests: 7 functions covering move (including array-index shift), copy, deep-copy independence, test pass/fail, and whole-patch abort. All fail against the unmodified engine except TestOp_Passes, which cannot distinguish "test evaluated and passed" from "test silently skipped" -- TestOp_Fails and TestOp_Fails_AbortsWholePatch carry that proof. The deep-copy test initially passed with deepCopyJSON removed entirely, because Properties is a JSON string and the round-trip destroys aliasing between calls. Rewritten to do the copy and the mutating replace in one patch document at nesting depth 2, which is the only way aliasing is observable. It now fails both when deepCopyJSON is dropped at the call site and when its map branch is made shallow. Closes: gopherstack-j6lv Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Generated by cmd/gendocs, not hand-edited. CI's docs job runs make docs then git diff --exit-code, so this had to land before the branch could pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…rincipal isExternalPrincipal treated every principal that was not a bare 12-digit account ID as external, so a same-account IAM role or user ARN was misjudged. Two consequences: AssociateResourceShare wrongly rejected such a principal when AllowExternalPrincipals was false, and when it was allowed the backend fabricated a pending invitation to the caller's own account. AllowExternalPrincipals gates OTHER ACCOUNTS, not IAM identities inside this one -- "Specifies whether principals outside your organization in Organizations can be associated with a resource share" (ram@v1.39.4 api_op_CreateResourceShare.go). AssociateResourceShare's own Principals doc lists "An ARN of an IAM role, for example: iam::123456789012:role/rolename" and the IAM user equivalent as valid principal forms. Now an ARN whose service segment is iam is external only when its account segment differs from this backend's account. Organization and OU ARNs stay unconditionally external even when their account segment matches, since an org/OU principal can admit arbitrary other member accounts -- that carve-out has its own regression subtest so a later simplification cannot quietly widen the exemption. On the issue as filed: the premise is confirmed. Nothing outside services/ram consults a resource share before granting access -- the only importers are cli.go, cli_test.go, dashboard/ui.go and internal/teststack, all registration boilerplate, and cli.go's wireTaggingRAM reads only ResourceShare.Tags for GetResources, never an association. No enforcement hook was built, and that is deliberate: every real RAM use case is cross-account, and this backend cannot represent one. CreateResourceShare unconditionally sets OwningAccountID to b.accountID (resource_shares.go:53), which makes listSharedWithMe dead code by construction. services/mq, services/managedblockchain and services/ce each already document the same single-account limitation independently. Gating the one same-account principal kind would mean routing through iam.EnforcementMiddleware in front of all ~150 services -- far beyond this issue. Recorded in PARITY.md rather than faked. Verified by neutering the iam-ARN branch (resource_shares.go:279), still compiling: the two same-account subtests and TestAllowExternalPrincipals_FalseAllowsSameAccountIAMPrincipal fail, while the organization-ARN and other-account subtests keep passing. Blast radius: this is an authorization check, so the full go test ./services/... was run -- no failures. No pre-existing test was modified. Closes: gopherstack-q91e Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…t filters
matchesJSONPattern compared only top-level pattern fields, so the common
real-world shape -- {"dynamodb":{"NewImage":{"id":{"S":["1"]}}}} -- could never
match past its first key. filter.go documented this as future work, and the
issue's premise is confirmed.
A pattern field's value is now either a nested object, which recurses a level
into the event, or an array of exact-match values and content-filter objects.
Fields at one level are ANDed, array entries for one field are ORed, per AWS's
eb-event-patterns-content-based-filtering.html. The SDK carries no grammar of
its own -- pipes@v1.26.4 types.go declares Filter.Pattern as a bare *string --
which is why the EventBridge docs are the source, cited in both the code and
PARITY.md.
Added numeric and cidr; prefix, suffix, anything-but and exists already
existed. Unsupported operators (wildcard, equals-ignore-case, $or) and any
unrecognized matcher object fail CLOSED rather than matching everything, which
is the safer direction for a filter and is pinned by its own test.
Exact matching is now type-sensitive: the old code fell back to comparing a
quote-stripped raw value against the rule string, so pattern "5" matched event
5. EventBridge does not do that, and the old path would also have panicked
under == on a decoded array or object. Now compared with reflect.DeepEqual on
decoded values. No pre-existing test covered the lenient behavior, so nothing
had to change -- but it is a deliberate behavior change, and neither it nor the
DeepEqual non-comparable guard has a dedicated test. Filed as follow-up.
services/eventbridge/pattern.go already has a nested matcher, but every
function in it is unexported and pipes is a separate package, so this pass
extended pipes' own json.RawMessage matcher rather than opening up
eventbridge's. numeric and cidr were ported algorithmically from it to stay
semantically aligned. Two matchers in one repo is a real duplication cost;
consolidating into a shared package is filed as follow-up rather than done
here, since it reaches files outside this issue's scope.
Verified by neutering, both still compiling: isJSONObject (filter.go:103)
forced false fails the three nested subtests plus every pre-existing operator
subtest, confirming it is load-bearing throughout; the fail-closed fallthrough
(filter.go:245) turned to true fails exactly
unrecognized_matcher_object_never_matches and nothing else.
Regression test TestFilter_NestedPatterns, 14 subtests. No pre-existing test
was modified beyond one comment line.
Closes: gopherstack-a2vk
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ipes follow-ups Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
… as unrepresentable gopherstack-vsmv: DeleteOutpost cleaned up runningInstances but left every renewalIdempotency entry for the deleted Outpost in place for the life of the process. Now pruned. The prune is safe rather than merely low-risk. Entries are keyed o.ID + "::" + ClientToken (renewals.go:60), so a prefix match on o.ID is exact and needs no extra index. More importantly there is no premature-eviction window: CreateRenewal calls resolveOutpostLocked BEFORE consulting the cache (renewals.go:71-77), so once the Outpost is gone a retried request fails at notFoundError and can never reach renewalIdempotency. The pruned entries were already unreachable, making this memory hygiene, not a behavior change. Orders and Quotes are deliberately NOT pruned, matching the pre-existing DeleteOutpost behavior -- they are historical records, whereas this is an idempotency cache. gopherstack-glw7: the prior pass declined to implement UpdateSiteAddress's "after all Outposts that belong to the site have been deactivated" clause, unable to tell from the SDK whether it is an independent gate or a paraphrase of no-order-in-progress. That judgment is confirmed, with new evidence that settles it more firmly than "unconfirmed": grep -rni "deactiv" across the entire outposts@v1.66.1 module matches exactly one line -- the doc sentence itself (api_op_UpdateSiteAddress.go:18). "Deactivated" names no lifecycle state anywhere in the SDK; types.Outpost.LifeCycleStatus is a bare *string with no enum backing. UpdateSiteAddress declares only AccessDeniedException, ConflictException, InternalServerException, NotFoundException and ValidationException -- ConflictException being the same generic type already used for the order-in-progress check, with no distinguishing code. So the second clause is not merely unconfirmed, it is unrepresentable here: an Outpost in this backend is ACTIVE, PENDING_DECOMMISSION, or deleted, and never occupies a third still-existing "deactivated" state. Implementing it would mean inventing what the word means. Recorded in PARITY.md's structural_gaps with what would actually settle it. No behavior change. Regression test TestDeleteOutpost_PrunesRenewalIdempotencyCache asserts the cache shrinks, reading it through a test-only accessor in export_test.go (the sanctioned convention here; no new exported seam in production code). Neutering the prune's HasPrefix guard (outposts.go:181), still compiling, fails that test alone -- TestDeleteOutpost and TestDeleteOutpost_CleansRunningInstanceLedger keep passing. Closes: gopherstack-vsmv Closes: gopherstack-glw7 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…guard
gopherstack-a2vk changed matchesExactRule from a string-coercing comparison to
reflect.DeepEqual on decoded values, making exact matching type-sensitive:
pattern "5" no longer matches event 5, and "true" no longer matches true. That
is correct per EventBridge, but nothing tested it in either direction -- no
pre-existing test covered the old lenient behavior either, which is why the
change passed silently.
TestFilter_ExactMatchTypeSensitivity now pins both halves, plus the reason
matchesExactRule uses reflect.DeepEqual rather than ==: == compiles fine on two
any values and panics at runtime on a non-comparable dynamic type, so a future
simplification would reintroduce a panic on malformed input.
Sensitivity proven by neutering, both still compiling. Restoring the old
lenient comparison fails string_pattern_vs_numeric_value_no_match and
string_pattern_vs_bool_value_no_match while the other three pass. Swapping
DeepEqual for == panics with "comparing uncomparable type []interface {}" in
array_pattern_element_vs_array_value_no_match_no_panic -- the stack runs from
the runner's poll loop through applyFilters into matchesExactRule with two
[]interface{} operands, confirming the case drives the real path rather than
passing vacuously.
Test-only change; filter.go is untouched.
Closes: gopherstack-50hq
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…king Delete ops
gopherstack-url6: GetChangeToken minted a fresh UUID on every call, contradicting
waf@v1.33.4 api_op_GetChangeToken.go:23-27 -- "If your application submits a
GetChangeToken request and then submits a second GetChangeToken request before
submitting a create, update, or delete request, the second GetChangeToken request
returns the same value as the first GetChangeToken request."
A single outstanding PROVISIONED token per backend is now held and returned until
MarkChangeTokenUsed consumes it, after which the next call mints a fresh one. The
field is guarded by the same coarse b.mu as the rest of the backend state and is
cleared in Reset.
TestWAF_ChangeToken_Unique was a pre-existing test asserting the AWS-contradicting
behavior (two calls never match). Rewritten to assert the real contract in both
directions: same-until-consumed, then new-after-consumed. Both halves are
independently load-bearing -- neutering the reuse (change_tokens.go:23) fails the
first, neutering the clear (change_tokens.go:66) fails the second.
TestChangeTokenStatus_Lifecycle/DeleteWebACL_transitions_to_INSYNC also broke,
because its mutate closure created a WebACL that fetched its own token and, under
the fix, stole the outstanding token the test had reserved for the delete. Fixed
by adding a setup step that creates the ACL and consumes its token BEFORE the
outer fetch. All 8 subtests keep their names and their wantBefore/wantAfter
assertions -- restructured, not weakened.
gopherstack-y6ok: the tag leak is real but the issue overstated it. 5 of the 12
Delete ops already cleared b.tags from an earlier same-day pass; the 7 match-set
families genuinely leaked, and had no ARN helper at all. Added the helpers and the
clears. Revert-proof shows exactly that split: the 7 fail against reverted code,
the 5 keep passing.
ListTagsForResource's missing existence check was deliberately NOT implemented.
WAFNonexistentItemException is declared on the op, but with the identical generic
type-level doc string ("The operation failed because the referenced object doesn't
exist.", types/errors.go:440) that it carries on ops which unambiguously do check
and on TagResource/UntagResource -- so its presence pins nothing about this op, and
api_op_ListTagsForResource.go adds nothing. Recorded as a gap rather than guessed
at.
The 7 new ARN resource paths follow the established lowercase-concatenated
convention of the 5 pre-existing helpers. No ARN example exists anywhere in the
pinned module to verify against literally; they are the backend's own tags-map key,
generated and consumed by the same helper, so internal consistency is what governs.
Noted in PARITY.md.
backendSnapshot gained OutstandingChangeToken. Purely additive with omitempty, so
wafSnapshotVersion stays at 1 -- the guard hard-fails a bump in the additive case --
and the golden inventory was refreshed with -update, a single added line.
Closes: gopherstack-url6
Closes: gopherstack-y6ok
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…up Description CreateScheduleGroup accepted a Description, stored it, persisted it, and GetScheduleGroup returned it. Real EventBridge Scheduler has no such member anywhere on the group ops: CreateScheduleGroupInput carries only Name, ClientToken and Tags (scheduler@v1.20.4 api_op_CreateScheduleGroup.go:31-42), GetScheduleGroupOutput carries only Arn, CreationDate, LastModificationDate, Name and State (api_op_GetScheduleGroup.go:39-60), and types/types.go has no ScheduleGroup type at all -- only ScheduleGroupSummary, which also has no Description. awsRestjson1_serializeOpDocumentCreateScheduleGroupInput (serializers.go:254-271) emits only ClientToken and Tags, so a real client cannot send the field even if it wanted to. Description IS real on CreateScheduleInput (api_op_CreateSchedule.go:89), for schedules rather than groups, which is the probable copy-paste origin. The scheduler audit had filed this as an additive extra field not worth fixing. That reasoning does not hold. This repo already treats a fabricated request field as a real parity bug -- gopherstack-emho removed CreateClusterSubnetGroup's invented VpcId on exactly that basis -- and this case is strictly worse, because the field was not merely accepted but returned. Client code reading Description back from GetScheduleGroup worked locally and would silently fail against real AWS, which is precisely the divergence a parity emulator exists to prevent. Removed end to end: handler input and output shapes, the backend signature, the model field, and the persisted struct. Four pre-existing tests asserted the fabricated shape and were corrected, not deleted -- they were entrenching it, the same pattern this repo hit with redshift's BatchDeleteClusterSnapshots and ssm's AddedLabels. TestCreateScheduleGroup_WithDescription, which asserted the Description round-tripped, is now TestCreateScheduleGroup_DescriptionNotAccepted and asserts the key is absent from the response. The other three had their Description assertions replaced with assertions on surviving fields (State, Name), not dropped. No snapshot version bump, deliberately. Removing a persisted field is not the additive case and the guard correctly refuses to assume bookkeeping -- but a bump here would be actively harmful: Restore discards the entire snapshot and starts empty on version mismatch (persistence.go:225-234), so bumping would destroy every user's schedules and groups to avoid carrying one fabricated key. No DisallowUnknownFields exists in the restore path, so an older snapshot's leftover "description" decodes as an ignored unknown key with every surviving field intact. Verified by the inverse of a neuter: re-adding Description to getScheduleGroupOutput, still compiling, fails TestCreateScheduleGroup_DescriptionNotAccepted. Closes: gopherstack-ui6k Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
CreateVpc created only the default security group. AWS creates a main route table for every VPC and deletes it with the VPC (ec2@v1.319.1 api_op_DeleteVpc.go:16), and this backend modeled neither the table nor any way to discriminate one -- RouteTable had no Main field at all. "Main" is a per-association property, not per-table: types.RouteTableAssociation carries "// Indicates whether this is the main route table. Main *bool", and SubnetId's doc reads "A subnet ID is not returned for an implicit association". So the main table is modeled as always carrying one implicit association with an empty SubnetID and Main=true, and Main is surfaced unconditionally on DescribeRouteTables associations. RouteTable also gets an internal Main bool with no SDK equivalent -- it is the discriminator the dependency-violation carve-out needs. Implemented: the main table per VPC with a local route (GatewayID "local", api_op_ReplaceRoute.go:77), its implicit association, and Main on the wire. Deliberately left absent and documented in PARITY.md: main-table reassignment via ReplaceRouteTableAssociation (rejected outright rather than half-done), the association.main DescribeRouteTables filter, a main table for the seeded vpc-default and CreateDefaultVpc (which bypass CreateVpc and whose fixture shape hundreds of unrelated tests depend on), and local routes on custom tables. DeleteRouteTable and DisassociateRouteTable now refuse to remove the main table and its implicit association. Neither op declares a specific error in the SDK -- extraction returns only "UnknownError" for both -- so these reuse the existing ErrDependencyViolation/ErrInvalidParameter sentinels rather than invent an unverified AWS code. The landmine from the issue was real. vpcIndexedDependencyViolationLocked rejected DeleteVpc whenever the VPC had any route table, so registering a main one would have broken every DeleteVpc in the repo. It now blocks only on a non-main table, mirroring the existing default-security-group carve-out, and DeleteVpc cascades the main table the way it already cascades the default SG. Defeating that carve-out (vpcs.go:388) fails every DeleteVpc test, confirming it is load-bearing. Also fixes gopherstack-97tc, a latent defect this work would have armed: ReplaceRouteTableAssociation used subnetID != "" as its found-sentinel and spliced the association out of its old table BEFORE checking it. Harmless while every association had a subnet, but the implicit main association has an empty SubnetID, so passing its ID would have detached a VPC's main route table and then returned ErrAssociationNotFound -- a destructive no-op reporting failure. The lookup now tracks found explicitly and mutates only after validation. All four new guards verified by individual neuter, each still compiling: vpcs.go:388 fails every DeleteVpc test; route_tables.go:85 fails TestDeleteRouteTable_MainRouteTableRejected; route_tables.go:212 fails TestDisassociateRouteTable_MainAssociationRejected; ec2core.go:401 fails TestReplaceRouteTableAssociation_MainAssociationRejected. TestDeleteRouteTable_MainRouteTableRejected initially passed under its neuter -- with the main guard defeated the next check (associations non-empty) returned the same ErrDependencyViolation sentinel, so an ErrorIs assertion could not tell them apart. Strengthened with a message assertion so it now discriminates the two rejection paths. Closes: gopherstack-y71o Closes: gopherstack-97tc Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Single golden covering all services, so one -update run necessarily carries both
of this round's persisted-shape changes. Three lines, no collateral:
+ RouteAssociation.Main (ec2, gopherstack-y71o)
+ RouteTable.Main (ec2, gopherstack-y71o)
- persistedScheduleGroup.Description (scheduler, gopherstack-ui6k)
Deliberately no version bump for either.
ec2 is the additive case the guard itself calls bookkeeping -- every old field is
present unchanged.
scheduler removes a field, which the guard correctly refuses to wave through
("this is NOT the additive case ... Confirm whether a version bump is actually
required"). It is not required, and bumping would be actively harmful: Restore
discards the entire snapshot and starts empty on version mismatch
(services/scheduler/persistence.go:225-234), so a bump would destroy every user's
schedules and groups in order to avoid carrying one fabricated key. No
DisallowUnknownFields exists in the restore path, so an older snapshot's leftover
"description" decodes as an ignored unknown key with every surviving field
intact.
Refreshed only after both contributing changes were committed, so no in-flight
work was baked into the golden.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Generated by cmd/gendocs, not hand-edited. Picks up the gap counts recorded by gopherstack-url6/y6ok (waf: the ListTagsForResource existence check left unimplemented) and gopherstack-glw7 (outposts: UpdateSiteAddress's deactivation clause recorded as unrepresentable). CI's docs job runs make docs then git diff --exit-code, so this had to land. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ange gopherstack-uifk removed ScheduleGroup's fabricated Description, changing InMemoryBackend.CreateScheduleGroup from (ctx, name, description, tags) to (ctx, name, tags). cli_test.go:2153 still passed the old four arguments, so the root package's test build broke. Missed because the agent that made the change was scoped to services/scheduler/ and never saw the root test file, and my gates could not catch it: go build ./... does not compile test files, and go test ./services/... excludes the root package. Only golangci-lint on ./ surfaced it, as a typecheck error. Closes: gopherstack-ui6k Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…te names
gopherstack-5c3m: RegisterInstancesWithLoadBalancer only checked instance-ID
FORMAT (i-[a-f0-9]{8,17}), never existence, so any well-formed but nonexistent
ID registered successfully. InvalidInstance is genuinely declared on the op --
extraction returns InvalidInstance, LoadBalancerNotFound, UnknownError -- and
maps to InvalidEndPointException ("The specified endpoint is not valid.",
elasticloadbalancing@v1.36.4 types/errors.go), an SDK naming quirk whose wire
code is real. The ErrInvalidInstance sentinel already existed for
DescribeInstanceHealth.
The issue's title said no hook was wired, but EC2Resolver already existed
(services/elb/crossservice.go) with SecurityGroupExists/SubnetExists, already
connected by cli.go's wireELBCrossService. So the convention was established,
not absent; this extends it with InstanceExists rather than inventing a parallel
mechanism, and cli.go's elbEC2ResolverAdapter gains the matching method.
A nil resolver stays a silent no-op, never a rejection -- ~150 services build
backends in tests with no cross-service hooks, and a nil-means-reject would
break them and change default behavior for anyone not running a full stack.
Pinned by no_resolver_wired_accepts_any_id.
gopherstack-ogvw: the premise held. builtinPolicyTypes() already models the full
PolicyAttributeTypeDescription schema per type, and PolicyTypeName was already
validated, but nothing checked a submitted attribute NAME against that schema --
any name and value was accepted and stored. Now rejected with
ErrInvalidConfiguration (InvalidConfigurationRequestException, "The requested
configuration change is not valid.").
Cardinality is deliberately NOT enforced, despite the SDK documenting its values
(types/types.go:483-489, "ONE(1) : Single value required" etc).
ProxyProtocolPolicyType's sole attribute is Cardinality ONE with a DefaultValue,
and this package's pre-existing tests create that policy supplying zero
attributes and expect success. A literal reading would break correct established
behavior, so name validation only, with the rest documented in PARITY.md.
Verified by neutering each guard, both still compiling: instances.go:43 fails
unknown_instance_rejected alone, leaving no_resolver_wired_accepts_any_id and
known_instance_accepted passing; policies.go:265 fails
unknown_attribute_name_rejected alone.
Blast radius: enforcement change, so the full go test ./services/... was run --
no failures.
Closes: gopherstack-5c3m
Closes: gopherstack-ogvw
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ventbridge and pipes gopherstack-a2vk left services/eventbridge/pattern.go and services/pipes/filter.go each carrying a nested EventBridge event-pattern matcher for the same specification -- pipes@v1.26.4 declares Filter.Pattern as a bare *string with no grammar of its own -- with numeric and cidr ported by hand and drifting. Consolidation is partial, deliberately. The two matchers diverge in ways a single implementation cannot absorb without changing one service's behavior: - Operator sets differ. eventbridge supports wildcard, equals-ignore-case, $or and object-form anything-but; pipes supports none of those. Filed as gopherstack-5eok rather than silently changed, since closing it changes what pipes matches. - Validation architecture differs fundamentally. eventbridge rejects an unrecognized matcher for the WHOLE pattern at compile time, matching real AWS (PutRule returns InvalidEventPatternException); pipes has no creation-time validation at all and fails closed per rule-array element at match time. Filed as gopherstack-sphp. - Exact-match comparison differs only in appearance: eventbridge's == is safe because validateMatcherArray restricts element types at compile time, while pipes needs reflect.DeepEqual because it has no such gate. So only what was genuinely duplicated moved: the numeric-comparison operator switch with its rules-come-in-pairs loop, ToFloat64, and the CIDR range check. compareNumeric was a byte-for-byte duplicate; the CIDR logic differed only in how it decoded its strings. Net 103 lines deleted from the two services for 16 added, plus an 80-line shared package. Verified both services genuinely route through the shared code rather than leaving it dead beside two live copies: inverting pkgs/eventpattern's MatchCIDR return, still compiling, fails BOTH suites -- eventbridge's package tests and pipes' cidr_operator_matches/cidr_operator_no_match. No test file in either service was touched, and no behavior changed anywhere, so neither PARITY.md gained a note claiming a fix. Blast radius: shared pkgs/ change, so the full go test ./services/... was run on a clean tree -- no failures. Closes: gopherstack-amfu Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
P1. A pattern of {"foo": [{"anything-but": [{"x":1}]}]} PASSED compilePattern
validation and then panicked at match time with "comparing uncomparable type
map[string]interface {}". matchAnythingBut's list case used
slices.Contains(ab, eventVal), which compares with ==, and validateMatcherObject
checked only that anything-but was a KNOWN KEY, never the SHAPE of its value.
So a pattern author could put an object or array inside the list, and when the
event field's value had the same non-comparable dynamic type, == panicked.
Nothing recovers around matchCompiledPattern and delivery.go calls it on the
PutEvents hot path, so this crashed the request rather than failing closed.
Fixed on both levels, deliberately.
Validation now rejects the shape at compile time. AWS's content-filtering
documentation states "You can use anything-but matching with strings and numeric
values, including lists that contain only strings, or only numbers"
(eb-event-patterns-content-based-filtering.html#eb-filtering-anything-but, cited
in the code), so a map or array element is not something real AWS accepts, and
PutRule/TestEventPattern returning InvalidEventPatternException matches its
behavior. Object-form anything-but keys are validated against the same list
matchAnythingButObject recognizes, so an unrecognized inner key is rejected at
creation instead of silently never matching.
Matching is now panic-safe regardless, via reflect.DeepEqual instead of ==.
Validation only constrains patterns compiled through compilePattern; a crash on
the delivery path must not be one loosened validator away. This is the same
guard, for the same reason, as services/pipes/filter.go's matchesExactRule --
the identical defect class, on the side that had the test.
Post-fix behavior for the repro input: rejected at PutRule/TestEventPattern, and
never reaches matching.
Neighbours swept. matchAnythingBut's default branch cannot receive a
non-comparable type -- []any and map[string]any are peeled off by earlier cases,
and Go only panics when both dynamic types are identical and non-comparable. The
exact-match defaults in matchObjectField and matchSingleValue are reached only
after validateMatcherArray has excluded those types. pattern.go:424 was the only
unsafe site.
The two fixes are independently pinned, each by its own test. Neutering
DeepEqual back to == panics only
TestPattern_AnythingBut_DefenseInDepth_NoPanicWhenValidationBypassed, since
validation still blocks the main repro; neutering validateAnythingButValue to a
no-op fails only TestPattern_AnythingButNonScalarListElement_RejectedAtCompile,
since DeepEqual keeps matching safe. Both neuters compile.
Found by the gopherstack-amfu duplication audit rather than a bug hunt, and
reproduced independently before this fix was written.
Closes: gopherstack-lrgk
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…d status constants gopherstack-7z3p: the SDK's deserializer reads failureReason for DatasetGroup (personalize@v1.50.4 deserializers.go:11024, in awsAwsjson11_deserializeDocumentDatasetGroup), but datasetGroupToMap omitted it unconditionally -- while its own sibling datasetGroupSummaryToMap already emitted it conditionally. An internal asymmetry, now fixed to match. This does not make the field observable through the live API today, because nothing sets it (see below). It makes the Describe shape correct for when it is set, matching the accepted SolutionVersion.FailureReason precedent of a field modeled but not yet populated. Fourteen personalize types carry FailureReason in the SDK; only DatasetGroup and SolutionVersion have it on the backend model at all, and the rest are already documented as absent-not-fabricated. Not reopened here. gopherstack-h3th: mostly a non-bug, and deliberately left that way. Five of the seven status constants are unreachable BY CONSTRUCTION in a synchronous, single-process emulator -- every Create* completes atomically so there is no provisioning phase to be pending in or to fail independently, every Delete* removes the resource with no pending-delete window, and StopRecommender already jumps ACTIVE<->INACTIVE the way StopSolutionVersionCreation jumps to CREATE STOPPED. Building a timer-driven CREATE PENDING -> IN_PROGRESS -> ACTIVE machine would fabricate AWS behavior rather than emulate it, and would make the suite slow and flaky in a repo that bans time.Sleep in tests. Recorded as unreachable-by-construction in PARITY.md instead. The real finding inside that non-bug: statusUpdatePending and statusUpdateProgress were not merely unreachable, they were FABRICATED. "UPDATE PENDING" and "UPDATE IN_PROGRESS" appear nowhere in the pinned SDK -- not in enums, types, or serializers for any of its Status-bearing types -- so had anything ever assigned them the backend would have emitted a status no real client could receive. Same defect class as the "STOPPED" vs "CREATE STOPPED" trap this file already documents. Removed. No regression test accompanies the constant removal: nothing ever assigned them, so there is no observable behavior to lock. The shape fix is pinned by TestDatasetGroupToMap_FailureReason -- neutering the conditional (handler_dataset_groups.go:79), still compiling, fails the present subtest while absent keeps passing. Landmine recorded in PARITY.md: the store.go doc comment's placement matters to the unused linter's grouping heuristic. A standalone comment above statusStopPending breaks contiguity with the used statusSolutionVersionStopped sibling and triggers a false unused finding, which is why the verdict lives in one block-level comment. Closes: gopherstack-7z3p Closes: gopherstack-h3th Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
A malformed pipe filter was accepted silently and then simply never matched at
delivery time, rather than surfacing an error the way eventbridge's PutRule and
real AWS Pipes both do. CreatePipe and UpdatePipe now reject a structurally
invalid pattern with ValidationException, which both ops declare.
AWS validates here. Its docs are explicit (eb-pipes-event-filtering.html,
"Filtering Kinesis and DynamoDB messages"): "Non-JSON | Any | EventBridge throws
an exception at the time of Pipe creation or update. The filter pattern must be
valid JSON format." The SDK's own validators.go never inspects FilterCriteria,
so client-side validation alone would not have settled it.
Corrects the premise this issue inherited from gopherstack-amfu. That framing
said pipes supports none of $or/wildcard/equals-ignore-case/object-form
anything-but. AWS's operator-support table (eb-create-pattern-operators.html,
"Pipe support" column) shows real Pipes DOES support $or and equals-ignore-case;
only bare wildcard and anything-but's object-negation forms are genuinely
EventBridge-only. gopherstack's filter.go simply has not implemented $or or
equals-ignore-case yet. gopherstack-5eok has been updated with this correction.
That distinction drove the design. Reusing eventbridge's isKnownMatcher would
have accepted $or and equals-ignore-case as valid while filter.go can never
match them -- CreatePipe saying yes and delivery silently never matching, which
is worse than no validation at all. So isKnownPipeMatcher mirrors exactly the
six keys filter.go's matchesRuleObject dispatches on, and anything the runtime
cannot match is rejected loudly at creation instead. Non-JSON substring
patterns, this backend's documented backward-compatible mode, are untouched:
only {-prefixed patterns get structural validation.
Creation-time validation and runtime fail-closed matching are independent
layers, as gopherstack-lrgk established for eventbridge, and both are pinned
separately. Neutering validateFilterCriteria fails 22 subtests across the two
ops while the runtime fail-closed tests keep passing, proving they exercise the
matcher on their own rather than riding on the new rejection.
Three pre-existing tests asserted patterns the new validation rejects at
creation. All three were preserved, not weakened: same wantMatch: false
assertions, with the pipe now built empty and the invalid pattern injected via
SetFilterPatternForTest so filter.go's fail-closed path is still exercised. The
third, TestFilter_ExactMatchTypeSensitivity's non-comparable-array case, was not
named in the brief -- the agent found it and applied the same treatment.
Landmine, recorded in PARITY.md and on gopherstack-5eok: isKnownPipeMatcher must
stay in lockstep with matchesRuleObject. Implementing an operator without
relaxing the validator in the same commit leaves CreatePipe rejecting patterns
the runtime can newly handle.
Closes: gopherstack-sphp
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…stUsers UpdateUserCustomPermission wrote to b.userCustomPermissions and DeleteUserCustomPermission cleared it, but DescribeUser and ListUsers built their responses purely from storedUser.toUser() and never consulted that map. A client could set a custom-permissions profile and never observe it. The field is real and this is the only way to read it. types.User carries "// The custom permissions profile associated with this user. CustomPermissionsName *string" (quicksight@v1.123.1 types/types.go:23202), and both DescribeUserOutput.User and ListUsersOutput.UserList are types.User. There is no DescribeUserCustomPermission op -- the SDK has DescribeAccountCustomPermission and DescribeRoleCustomPermission but no user equivalent -- so DescribeUser/ListUsers are the only read path, which is what makes this write-only rather than readable-elsewhere. Emitted only when non-empty, matching the serializer's omit-when-nil behavior; no empty-string emission. Also fixes gopherstack-3tju, an adjacent bug that this change would otherwise have made visible rather than merely latent: DeleteUser and DeleteUserByPrincipalID removed the user but left the userCustomPermissions entry behind. That map is keyed by user NAME, so re-registering a user with the same name in the same namespace silently inherited the deleted user's profile. Dormant while nothing read the map; live the moment the read path exists. Fixed in the same commit because shipping the read alone would have introduced the defect, and filed separately per the campaign rule. Verified by neutering the wire emission (handler_user.go:217), still compiling: describe_user_surfaces_it and list_users_surfaces_it fail while absent_before_update and absent_again_after_delete keep passing. RegisterUser and UpdateUser return *User through the same converter and were left alone -- the issue named only the two read ops. UpdateUser is recorded in PARITY.md as a smaller residual gap. Two //nolint:dupl directives were removed as dead. They were live before this change; nolintlint began flagging them only once ListUsers grew two lines and broke dupl's pairing with ListIngestions. Shrinking ListUsers back would require restoring them. Closes: gopherstack-rt14 Closes: gopherstack-3tju Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ted code Third cluster of gopherstack-yatn's orphan-code class. "InvalidAlgorithmException" has zero occurrences anywhere in kms@v1.55.4 -- not in deserializers.go, not in types/errors.go, not on an unrelated operation. GenerateMac (deserializers.go:2953) and VerifyMac (:6647) both declare InvalidKeyUsageException, whose doc comment (types/errors.go:753-767) names this exact condition: The encryption algorithm or signing algorithm specified for the operation is incompatible with the type of key material in the KMS key (KeySpec). [...] For generating and verifying message authentication codes (MACs), the KeyUsage must be GENERATE_VERIFY_MAC. Control ops Sign, Verify, Encrypt, Decrypt and ReEncrypt declare it too, and none of them declares InvalidAlgorithmException. validateMacAlgorithm has exactly two callers -- hmac.go:42 (GenerateMac) and hmac.go:102 (VerifyMac) -- so one shared remedy is right here, with no per-call-site split. ErrInvalidAlgorithm had no other user, so removing the sentinel outright closes both the wire mapping and the sentinel's own message string in one step; the cluster-2 gap where a fix touched only the handler mapping cannot recur here. Not authorization or enforcement: this is algorithm-vs-keyspec input validation inside HMAC generate/verify. No key-authorization, grant evaluation, or policy enforcement path is touched. Three pre-existing tests in hmac_test.go asserted the invented code. Corrected to InvalidKeyUsageException; strengthened, not weakened. Two new real-client regression tests added in mac_algorithm_wiring_test.go, both failing before the fix. crypto.go:825 neutered by line number: compiles, and all five algorithm tests fail. kms's 32 open ValidationException landmine comments (gopherstack-q9bs) were read for context and deliberately left untouched. The networkmanager half of this cluster is NOT a bug. InvalidPolicyDocument at corenetworks.go:47,175 sits in CoreNetworkPolicyError.ErrorCode, a *string with no enum, nested inside CoreNetworkPolicyException.Errors -- opaque per-item payload, not the wire discriminator. handler.go:247 sends the correct outer type CoreNetworkPolicyException, which both CreateCoreNetwork and PutCoreNetworkPolicy declare. Already declined at services/networkmanager/PARITY.md:51-58, re-verified at :1291-1303, and independently re-derived here. No files changed there. Closes gopherstack-xew9 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
errtargetaudit's orphan-code class flags BatchStopJobRun's "IllegalStateException". It is a per-entry batch code in a 200 body (jobs.go:496-503 appends into BatchStopJobRunOutput.Errors and continues; it never reaches a sentinel), and ErrorDetail.ErrorCode is an unconstrained *string with no enum, so no declared set applies. PARITY.md already noted the field was correctly populated but not that this audit class had been screened against it. Recording it so the next pass does not re-derive the same conclusion -- six passes in this campaign have been spent re-confirming already-declined findings. Refs gopherstack-yatn Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Fourth and final cluster of gopherstack-yatn's orphan-code class. ses DeleteReceiptFilter emitted "FilterDoesNotExist" for a missing filter. That string appears nowhere in ses@v1.37.4, and the operation declares nothing at all: awsAwsquery_deserializeOpErrorDeleteReceiptFilter is a default-only switch with no cases, and botocore's ses/2010-12-01 service-2.json carries no "errors" key on the op. A missing filter is now a no-op, matching the sibling precedent already established for DeleteReceiptRule, DeleteReceiptRuleSet and DeleteCustomVerificationEmailTemplate in undeclared_delete_errors_test.go -- DeleteReceiptFilter was simply missed by that sweep. ErrReceiptFilterNotFound had one raiser and is removed outright, closing mapping and sentinel string together. sts DecodeAuthorizationMessage emitted "InvalidParameter" for a missing required EncodedMessage. This one needed care not to trade one undeclared code for another: neither "InvalidParameter" nor "MissingParameter" is modeled in sts@v1.45.4, whose DecodeAuthorizationMessage declares only InvalidAuthorizationMessageException. The distinction is real anyway. MissingParameter is a genuine Query-protocol frontend code -- it is in errtargetaudit's genericProtocolCodes, gopherstack-udkm's per-entry audit confirmed zero modules model it per-op, and AWS's STS Common Errors page documents it for exactly this condition. Bare "InvalidParameter" is documented nowhere by AWS. Twelve sibling missing-parameter sentinels in the same switch already map to MissingParameter; ErrMissingEncodedMessage was the sole outlier. Not a credential-issuance, assumed-role or policy-decoding path -- error taxonomy only. Two pre-existing tests asserted the invented codes. sts's carried an uncited comment claiming "AWS returns InvalidParameter (not MissingParameter)", which is false. ses's asserted a 400 for a missing filter and is now renamed to say it is idempotent. Both corrected with the deserializer cite in a comment; strengthened, not weakened. Guards neutered individually by line number and confirmed load-bearing: a re-inserted not-found guard in receipt_rules.go fails all three ses tests, and restoring ErrMissingEncodedMessage to its own InvalidParameter case fails TestDecodeAuthorizationMessageEmpty. Both compile. Two findings in this cluster were NOT bugs and are recorded in PARITY.md rather than changed: workmail CreateImpersonationRole EntityAlreadyExistsException -- already declined at services/workmail/PARITY.md:83; the op models no AlreadyExists-shaped exception, so no replacement was invented. xray PutTraceSegments InvalidSegment -- a per-entry code on UnprocessedTraceSegment inside a 200 body. That field is a free-form *string, and the op dispatches only InvalidRequestException and ThrottledException as HTTP errors. Closes gopherstack-co3w Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ndmines gopherstack-i4q8 asserted that "ValidationException does not exist in the pinned kms module ... so all 36 sites emitting it are wrong", and left 32 landmine comments saying so. errtargetaudit's genericProtocolCodes allowlist asserted the opposite. Both were in the tree. This settles it against i4q8's framing. Evidence, verified against the vendored botocore-style model that aws-sdk-go-v2's generator consumes: aws-sdk-go@v1.55.8 models/apis/kms/2014-11-01/api-2.json -- zero occurrences of ValidationException in the whole file, not just per-op error lists. KMS's model genuinely never declares it at any level, which corroborates i4q8's original grep. The same directory's docs-2.json documents KMS returning it anyway, on the DER-encoded X.509 public key parameter shared by GetPublicKey and DeriveSharedSecret: "If you use Amazon Web Services CLI version 1, you must provide the DER-encoded X.509 public key in a file. Otherwise, the Amazon Web Services CLI Base64-encodes the public key a second time, resulting in a ValidationException." A double-Base64 blob is still well-formed Base64, so it passes client-side parameter validation and is rejected by the service. That is AWS's own documentation of a live wire ValidationException on an operation whose model does not declare it -- precisely the pre-dispatch, model-independent fault the allowlist exists for. kms@v1.55.4's deserializeOpError default case preserves an unmodeled wire code verbatim rather than rejecting it, which is what an SDK built to expect such codes looks like. i4q8's own key_agreement.go comment had already found this quote and dismissed it as "not a modeled type this SDK can deserialize" -- begging the question the allowlist exists to answer. So the 32 landmine comments assert something false and are removed. The four sites i4q8 actually remapped (to LimitExceededException and UnsupportedOperationException) stand on their own merits and are untouched, as are their justifying comments. Comments only: `git diff -U0 | grep` for any changed non-comment line returns nothing across all 17 files. No logic, error value, or control flow changed anywhere in kms; no map or logic change in either audit tool. The kms finding set is unchanged at 9. Recorded in both allowlists' doc comments so this is not re-litigated. Notably ValidationException was the one entry that had neither a brief citation nor a "confirmed live" reference -- only a bare "sibling of ValidationError" assertion. It now meets the standard the other entries were held to. I expected this to go the other way and said so when filing it; the evidence is the reason it did not. Closes gopherstack-q9bs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
gopherstack-jpfk asked whether ssm's reuse of a ValidationException-shaped sentinel for per-op input validation is a real defect class. Answer: no, with one small real exception concentrated entirely in PutParameter. Measured across ssm@v1.73.4's 152 deserializeOpError switches: exactly three operations declare ValidationException -- GetAccessToken, StartAccessRequest, StartExecutionPreview. The other 126 of 129 flagged sites guard fields the SDK's own input structs mark required (Smithy @required) or enum membership, and their operations declare nothing that fits. That is idiomatic: AWS validates structural constraints at the protocol front door regardless of per-op modeling, which is why only 3 of 152 ops model the code at all. Same verdict shape as gopherstack-mq6m's mgn probe, reached differently -- mgn was one funnel point counted many times, ssm is many independent decisions that are each individually correct. The exception is PutParameter, whose own declared set contains exact word-for-word matches for three of its four checks: validateParameterName -> ParameterPatternMismatchException "The parameter name isn't valid." Type enum check -> UnsupportedParameterType "The parameter type isn't supported." validateAllowedPattern -> InvalidAllowedPatternException "The request doesn't meet the regular expression requirement." Doc comments quoted verbatim from ssm@v1.73.4 types/errors.go. The fourth check (DataType) has no fitting declared code and keeps ValidationException with a landmine comment saying so. Two near-misses were rejected by reading the doc comment rather than the type name: InvalidParameters is about "values for all required parameters in the SSM document", not general required input, and InvalidAutomationSignalException is about a signal "not valid for the current execution", a runtime-state check rather than an unrecognized enum value. Each of the three helpers has a single call site, all inside validatePutParameterInput, which itself is called only by PutParameter -- so one remedy per helper is correct with no per-call-site split. classifySSMErrorExtended's if-chain became a loop over a classifier slice. Behaviour-preserving: same classifiers, same first-match-wins order, with the new group inserted second. It was at the cyclop budget and a seventh `if` would have exceeded it; this repo bans cyclop nolints. Four pre-existing assertions expected the generic sentinel, and one asserted only that __type was non-empty. All corrected to the specific declared code, each with the reason inline. Strengthened, not weakened. Three guards neutered individually by line number -- parameters.go:62, :141, :325 -- each compiles and fails its own test. ssm's distinct emission sites drop 129 to 126, exactly the three fixed lines; the (op, code) finding count stays 71 because PutParameter still has the deliberately-unfixed DataType site. PARITY.md's 2026-08-29 note had explicitly declined this same PutParameter fix for lack of confidence. Marked superseded rather than left to contradict the code. Not authorization-adjacent: error codes on input validation only, with no change to command targeting, session access, or enforcement. Noted and left alone as out of scope: GetParameters reports missing names through the shared resolveParameterSelector helper, where real AWS returns them in the output's InvalidParameters list rather than erroring at all. That is a response-shape redesign, not an error-code fix. Closes gopherstack-jpfk Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…eachable gopherstack-t8iz asked whether ErrInvalidRoutingConfiguration's fourth raiser can emit ValidationException from StartSyncExecution, which does not declare it. Verdict: genuinely unreachable through every path, including restore. No mapping change -- StartSyncExecution declares nothing that fits, and swapping one undeclared code for another is not a fix. The raiser and caller list matches what the issue claimed: aliases.go:15,25,36 (validateRoutingConfig) plus qualified_arn.go:92 (pickRoutedVersion), the latter reached via resolveExecutionTarget from executions.go:92 (StartSyncExecution) and :280 (StartExecution). No other callers exist. Every path that could produce an alias with empty RoutingConfiguration was checked: CreateStateMachineAlias validates routing before constructing the alias, and aliases.go:71 is the only StateMachineAlias construction site in the package. UpdateStateMachineAlias assigns only inside `if len(routing) > 0`, so an empty slice leaves the existing already-validated config untouched. This is doubly guarded: neutering just the length check still leaves validateRoutingConfig rejecting the empty slice. Restore cannot produce an alias at all. persistence.go's newPersistedDTORegistry covers only stateMachines, activities and executions; Restore leaves b.aliases as constructed, i.e. empty. So a hand-edited or older snapshot cannot reintroduce one. Two proof-of-unreachability tests added rather than a behavioural regression test, since there is no behaviour to change: TestUpdateStateMachineAlias_EmptyRoutingLeavesConfigUnchanged and TestAliasRoutingConfiguration_NotPersistedAcrossRestore. The first is load-bearing on aliases.go:108 -- neutering that line compiles and fails it. Snapshot format untouched, no version bump: persistence.go is unchanged. Closes gopherstack-t8iz Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
The tool reported one row per (operation, code) pair, which is not the
actionable unit. mgn showed 122 class A findings; gopherstack-mq6m spent a
whole agent pass discovering by hand that they collapse to four source lines,
two of which are generic plumbing reached by nearly every operation. A reader
could not tell a 90-op funnel point from 90 independent defects.
Each section is now grouped by file:line, with the op count, the mechanism
tag, and the ops behind it. mgn's 122 findings now read as exactly the four
lines mq6m found:
services/mgn/handler.go:372 InternalServerException 90/95 ops
[constructor classifier: internalServerError] -- SHARED PLUMBING
services/mgn/handler.go:361 ValidationException 32/95 ops
[constructor classifier: validationError] -- SHARED PLUMBING
services/mgn/applications.go:27 ValidationException 1/95 ops
services/mgn/waves.go:27 ValidationException 1/95 ops
The shared-plumbing threshold is 0.25 of a service's resolved ops, chosen
from the corpus rather than taste. Across all 354 site groups in 160
services the ratios are 0.947 (mgn marshalResponse), 0.337 (mgn
decodeJSONBody), then 0.162 (cloudfront quantity_validation.go:56, a real
but narrower site) and down. 0.25 sits in the empty gap, firing on exactly
the two mq6m-confirmed generic sites and nothing else observed. A
sharedPlumbingMinOps floor mirrors the existing minOpsForResolutionGuard so
the ratio cannot fire at small N.
The mechanism tag is now on every group line rather than only in the JSON.
Six of the ten residual orphan findings are the composite-literal shape --
a per-entry code written into a free-form *string inside a 200 body -- and
that class is now visible at a glance instead of requiring a JSON dive. The
new site key (File, Line, Code) is also strictly more precise than the old
causeKey (Code, Mechanism), which carried no location and could conflate
different files sharing a generic mechanism string.
Op lists are not truncated: the set-diff guard needs to reconstruct
(op, code, site) triples mechanically from stdout, and eliding names would
make that lossy. The header line alone carries the at-a-glance signal.
Presentation only -- scan.go, emit.go, classifiers.go and deser.go are
untouched. Verified by set-diff against a binary built from the pre-change
report.go and main.go: 360 class A, 10 orphan, 70 warnings both before and
after, and 510 unique (service, section, op, code, site) triples extracted
from stdout in each, identical in both directions.
sharedPlumbingRatio neutered at report.go:318 from 0.25 to 0.5: compiles,
fails TestIsSharedPlumbing_ThresholdFromCorpus, and drops the corpus tag
count from 2 to 1 as the decodeJSONBody funnel goes unflagged.
No nolint suppressions. A fieldalignment warning was fixed by reordering
struct fields and a goconst violation by naming two test constants.
Closes gopherstack-2evc
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Slice of gopherstack-jkma, from the findings gopherstack-udkm unmasked when it stopped genericProtocolCodes from suppressing ValidationException in the services that model it per-op. Neither service's flagged operations model it. Verified per-op, not in aggregate: of configservice@v1.68.4's 102 operations, 37 do declare ValidationException -- but none of the 19 flagged ones is among them, so every finding is a real mismatch and no fix displaced a validly declared code. efs@v1.44.4 declares it on only 4 of 31 operations (CreateReplicationConfiguration, DescribeBackupPolicy, DescribeReplicationConfigurations, PutBackupPolicy), none of them flagged. awsconfig: 19 findings across 22 raise lines in 8 files, all funnelling through the shared ErrValidation sentinel. This is the ssm shape -- many independent per-op decisions to reuse a generic sentinel -- not the mgn shape of one plumbing line counted many times, so it was worth working. Eight sites have a fitting declared code: seven ops declare InvalidParameterValueException, which this package already uses for exactly this pattern on PutRemediationExceptions and DeleteRemediationExceptions, and DescribeConfigRules declares InvalidNextTokenException, a word-for-word fit for its invalid-NextToken condition. The remaining 11 ops declare no validation-shaped code at all -- only not-found and conflict codes -- and keep ErrValidation with a landmine each rather than a guess. efs: 8 findings across 15 raise lines, mostly behind validateTags (CreateAccessPoint, TagResource, CreateTags, CreateFileSystem). All 30 of efs's 31 operations declare BadRequest -- the exception, DescribeAccount Preferences, is not flagged -- and its doc reads "Returned if the request is malformed or contains an error such as an invalid parameter value or a missing required parameter". An exact fit, and a shared remedy is safe here precisely because every caller declares it, unlike the kms helpers in gopherstack-4ra7. Precedent already in this service: PutFileSystemPolicy was swapped from ValidationException to InvalidPolicyException for the same reason in an earlier pass. Eleven pre-existing assertions had hard-coded the wrong sentinel, each locking in the defect being fixed here. All corrected to the specific declared code with the reason inline; every change makes the assertion more specific, none removes one. Three representative guards neutered by line number -- aggregators.go:78, handler_config_rules.go:88, tags.go:19 -- each compiles and fails its own test. awsconfig 19 findings -> 11, all intentional landmines. efs 8 -> 0. Closes gopherstack-rj8j Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…on is legal gopherstack-5rjn and gopherstack-4ra7, worked together as one cluster. CreateKey's declared set, re-derived from kms@v1.55.4, contains no key-usage-shaped code: CloudHsmClusterInvalidConfiguration, CustomKeyStoreInvalidState, CustomKeyStoreNotFound, DependencyTimeout, InvalidArn, KMSInternal, LimitExceeded, MalformedPolicyDocument, Tag, UnsupportedOperation and the three Xks codes. InvalidKeyUsageException is absent, and its own doc is about an existing key's KeyUsage being wrong for the operation invoked -- not a creation-time KeySpec/KeyUsage pairing. So it was never merely undeclared here; it was the wrong condition. validateKeySpecUsage now raises ErrUnsupportedParameter (UnsupportedOperationException), which CreateKey does declare and which import.go already uses for the same KeySpec-shaped rejection. Weighed ValidationException against gopherstack-q9bs's new evidence and declined: q9bs established a pre-dispatch structural fault (a malformed blob rejected before operation logic), whereas a KeySpec/KeyUsage pairing is cross-field operation logic. Different condition, so the better-evidenced declared code wins. The second site turned out not to be an error-code bug at all. Its premise was false: kms@v1.55.4's own api_op_CreateKey.go:83 states "You can create multi-Region KMS keys for all supported KMS key types: symmetric encryption KMS keys, HMAC KMS keys, asymmetric encryption KMS keys, and asymmetric signing KMS keys." The emulator was rejecting something AWS allows, so the check is removed rather than given a better code. That is a behaviour fix, not a taxonomy one. gopherstack-4ra7 gets no plumbing, deliberately. Both helpers' caller lists were re-verified by grep. None of validateEncryptionContextSize's seven callers declares a size-shaped code, so threading a sentinel through to choose between codes that all fail to fit would add cost for no gain. resolveKeyID's branch is confirmed unreachable -- both writers of keyIDResolutionCache always store cachedResolution, and Restore clears the cache rather than repopulating it -- so it is a landmine, matching the gopherstack-t8iz precedent. Both existing comments are strengthened in place with the caller lists and the unreachability proof. No behaviour changed at either site. Three pre-existing tests encoded the false premises. TestHandlerCreateKeyHMACMultiRegionRejected asserted a 400 for a legal request and is replaced by TestHandler_CreateKey_HMACMultiRegion_ViaHTTP, which asserts 200 and the round-tripped KeySpec, KeyUsage and MultiRegion -- strictly stronger than what it replaced. TestCreateKeyValidations' hmac_multiregion case flipped to wantErr:false, and TestKMSCreateKeyIncompatibleSpecUsage's assertion moved to the declared sentinel. Neutering found a coverage gap the sweep left: reverting keys.go:36 (the symmetric branch) fails TestKMSCreateKeyIncompatibleSpecUsage, but reverting keys.go:61 (the HMAC branch) passed everything -- that line's sentinel was unverified by any test. Added an HMAC_256_with_SIGN_VERIFY case; the neuter now fails on exactly that subtest. Re-adding the removed multi-Region rejection fails both HMAC multi-Region tests. All neuters compile. Not authorization or enforcement: request validation and error-code mapping only, with no key-authorization, grant-evaluation or policy-enforcement path touched. Closes gopherstack-5rjn Closes gopherstack-4ra7 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…0dw; correct kpk5 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…declare gopherstack-bzyl. The filing said the findings split across two codes; there are three. InvalidStateException (RevokeCertificate, 2 sites) was missing from my list entirely, so the slice was under-scoped as filed. RequestCertificate (9 sites): its deserializer declares InvalidArnException, InvalidDomainValidationOptionsException, InvalidParameterException, InvalidTagException, LimitExceededException, TagPolicyException and TooManyTagsException -- never ValidationException. Its exclusive validators (validateRequestCertInput, checkIdempotency, validateManagedBy, and the malformed-body case) now raise a new ErrRequestCertInvalidParameter. validateDomainName is shared with CreateAcmeDomainValidation, which DOES declare ValidationException, so it was parameterised rather than renamed globally -- the same shape as efs's validateTags, and the opposite of kms's validateEncryptionContextSize where no caller had a fitting code. setTags (3 sites): two length branches still hardcoded ErrInvalidParameter while the function already took an invalidTagErr parameter -- an incomplete parameterisation left by gopherstack-ftkd earlier the same day. Both now use the caller's sentinel. AddTagsToCertificate and RemoveTagsFromCertificate declare InvalidTagException. RevokeCertificate (2 sites): declares AccessDeniedException, ConflictException, InvalidArnException, ResourceInUseException, ResourceNotFoundException, ThrottlingException and ValidationException -- not InvalidStateException. The prior pass treated both guards as one pair; they are different conditions and get different answers. The PENDING_VALIDATION guard becomes ConflictException, whose doc is a direct match: "You are trying to update a resource or configuration that is already being created or updated. Wait for the previous operation to finish and try again." The already-revoked guard stays landmined: ValidationException is declared but its doc is about input failing constraints, and ResourceInUseException is about association with another service. Neither fits a terminal one-time state, and InvalidStateException is a real acm code elsewhere (ResendValidationEmail, UpdateCertificateOptions), so this is a right-code-wrong-op landmine rather than an invention. The six ResourceNotFoundException findings are NOT defects. They guard a certificate ARN that RequestCertificate minted synchronously in the same request before returning it, so they cannot fire. Already recorded under gopherstack-ftkd's root cause 3. Three pre-existing tests pinned the wrong codes, each locking in the defect being fixed. All corrected with the deserializer cite inline, and one renamed to say what it now asserts. Strengthened, not weakened. Three guards neutered by line number -- handler_tags.go:77, certificates.go:279, certificate_lifecycle.go:170 -- each compiles and fails its own tests. acm 20 findings -> 7: the 6 unreachable ResourceNotFoundException sites plus the one landmined InvalidStateException site. Closes gopherstack-bzyl Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Filed from a per-service audit row count without checking for a closed issue covering those services. Landmined findings still appear in audit output by design, so a nonzero row count is not evidence of untriaged work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
gopherstack-2i0c and gopherstack-39ip, both the campaign's third bug shape: a confirmed mismatch with no remedy anyone could evidence. Both stay landmined. The work was answering the specific evidence question each issue named, so the next pass does not redo it. memorydb CreateCluster emits SnapshotNotFoundFault for an unknown SnapshotName. Its declared set has a not-found fault for every OTHER referenced resource -- ACLNotFoundFault, ParameterGroupNotFoundFault, SubnetGroupNotFoundFault, MultiRegionClusterNotFoundFault -- but none for the snapshot. The open question was whether that asymmetry is a real AWS modelling choice or an artifact. It is real: the pinned memorydb@v1.36.4 deserializer and the live API_CreateCluster.html Errors section carry the identical 18 codes, so model staleness is ruled out. SnapshotNotFoundFault is a genuine memorydb code declared by CopySnapshot, DeleteSnapshot, DescribeSnapshots, ListTags, TagResource and UntagResource -- right code, wrong op. What is still missing is any doc sentence saying what CreateCluster actually returns for an unresolvable SnapshotName; InvalidParameterValueException's doc is the generic "The specified parameter value is not valid", which is a guess, not a match. So no swap. elb DeleteLoadBalancerPolicy emits PolicyNotFound and declares exactly InvalidConfigurationRequest and LoadBalancerNotFound. The prior pass declined to copy DeleteLoadBalancer's idempotent-success fix because that one rests on a doc sentence this op lacks; that negative claim is now verified rather than inherited. api_op_DeleteLoadBalancer.go:19-20 reads "If the load balancer does not exist or has already been deleted, the call to DeleteLoadBalancer still succeeds." DeleteLoadBalancerPolicy's entire doc is "Deletes the specified policy from the specified load balancer. This policy must not be enabled for any listeners." -- no equivalent, in the pinned comment or the live API reference. PolicyNotFoundException exists in the module and is semantically exact, but is declared only by DescribeLoadBalancerPolicies, SetLoadBalancerPoliciesForBackendServer and SetLoadBalancerPoliciesOfListener. Of the two declared codes, LoadBalancerNotFound names the wrong resource and InvalidConfigurationRequest is a stretch. So no swap. Comments only: `git diff -U0` on the .go files shows no changed non-comment line. No behaviour changed, which is why the two tests that deliberately pin the current wrong codes -- TestErrCode_CreateCluster_SnapshotNotFound and TestPolicyNotFoundReturns400 -- are correctly untouched. Both audit findings still report at the same lines. Closes gopherstack-2i0c Closes gopherstack-39ip Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…t double-counted gopherstack-s0dw. Site grouping (gopherstack-2evc) reports a shared helper's finding twice: once as a definition-site row -- the constructor's own sentinel return, reached by one-hop recursion from every caller -- and again as one call-site row per caller. cloudfront showed 29 rows for 27 distinct (op, code) pairs. A reader summing op counts sizes the population wrong. Measured before choosing a remedy, by AST function attribution rather than by matching on code text: a definition-site row duplicates call-site rows only when it sits inside the body of the very function those call sites invoke. Same-code-different-function coincidences are not duplicates, and a naive per-code check would wrongly fold them together. Corpus-wide the shape is rare and structurally consistent. 37 definition-site rows across 6 services (ssm 23, stepfunctions 4, kms 3, iot 3, acm 3, cloudfront 1). In 33 the definition-site op set equals the union of the matching call-site rows; in 4 it is a proper subset, caused by a caller that reaches the constructor only at hop 1, so finding its sentinel return would exceed maxEmitHop. The subset relation always holds in the same direction -- definition-site ops are a subset of the call-site union, never the reverse -- which is structurally guaranteed: an op reaches the constructor's own sentinel return only by having made the classified call first. Verified independently across all 37 tagged rows: every tag's claim holds, none names a function with no matching call-site rows, and no tagged row carries an op the call-site union lacks. So the remedy annotates rather than removes. A definition-site row now carries "-- ROLLUP: same ops as <fn>'s own call-site row(s) elsewhere in this list -- do not add to totals", or "-- PARTIAL ROLLUP: a subset of ..." for the four subset cases. Removing the row would have been the other option, but even in the subset cases it loses no ops, and keeping it preserves the sentinel-reference evidence a reader may want. Presentation only. Set-diff against the pre-change binary over (service, section, op, code, file:line) triples: 473 before, 473 after, comm -3 empty in both directions. Headline counts unchanged at 341 class A, 10 orphan, 70 coverage warnings. rollupTag neutered by early return at report.go:399: compiles, fails both TestPrintSiteGroups_RollupTag_ExactMatch and TestPrintSiteGroups_RollupTag_PartialSubset, and drops all 37 tags from the corpus. Closes gopherstack-s0dw Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
gopherstack-3hov. The seeded p-FullAWSAccess policy took its ARN from
policyARN, which keys to the caller's management account. That policy is
AWS-owned, not account-owned, so real AWS gives it the "aws" authority with
no account and no org segment.
Settled by botocore's own PolicyArn pattern, the shape behind
PolicySummary$Arn (aws-sdk-go@v1.55.8
models/apis/organizations/2016-11-28/api-2.json). It offers exactly two
alternatives and nothing else:
^(arn:aws:organizations::\d{12}:policy\/o-[a-z0-9]{10,32}\/[0-9a-z_]+\/p-[0-9a-z]{10,32})
|(arn:aws:organizations::aws:policy\/[0-9a-z_]+\/p-[0-9a-zA-Z_]{10,128})
The prior pass declined to special-case this for want of corroboration. The
pattern is the corroboration, and the repo already encodes the same authority
elsewhere -- services/ssoadmin/application_providers.go uses
arn:aws:sso::aws:applicationProvider/..., and services/ram/store.go:142 uses
arn:aws:ram::aws:permission/....
Scoped to the seeded policy only. New awsManagedPolicyARN builds the AWS-owned
shape; policyARN keeps building the customer-owned one and CreatePolicy still
uses it, since a customer policy correctly carries the account authority. A
blanket change would have been wrong.
Blast radius checked before editing rather than after. No map is keyed by the
ARN -- policyKeyFn keys on PolicySummary.ID -- and DescribePolicy takes a
policyID. The one place that compares ARNs, resourceIDForARNLocked in tags.go,
compares the input against the STORED PolicySummary.ARN field, so it resolves
whatever format is stored and stays correct across the change.
ListPolicies, ListPoliciesForTarget and handler_policies.go only echo the
stored value.
Snapshot format is unaffected: this is a value change, not a shape change, so
organizationsSnapshotVersion stays at 1 and no user snapshot is discarded. A
snapshot taken before this change restores with the old account-keyed string,
which is cosmetically inconsistent but not functionally broken, for the same
reason the lookup analysis above holds.
TestDefaultFullAWSAccessPolicy_ARN_ViaHandler asserts the ARN through the
handler. Neutered by reverting policies.go:128 to policyARN: compiles, and
fails with
expected: "arn:aws:organizations::aws:policy/SERVICE_CONTROL_POLICY/p-FullAWSAccess"
actual : "arn:aws:organizations::123456789012:policy/o-.../SERVICE_CONTROL_POLICY/p-FullAWSAccess"
Not an authorization change: an ARN string echoed on one policy, with no
policy attachment, effective-policy evaluation or enforcement path touched.
Closes gopherstack-3hov
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ignoring it gopherstack-3qel. ListDeploymentsInput models externalId (codedeploy@v1.38.4 api_op_ListDeployments.go:51, "The unique ID of an external resource for returning deployments linked to the external resource"), but gopherstack's wire struct never declared it. A client could pass the filter and get every deployment back, with no error and no way to tell the filter had been dropped. The prior pass left this unfixed because nothing here can populate the field. That much holds, and is now verified rather than assumed: grepping every *Input struct in the pinned module, only two operations carry ExternalId -- ListDeployments (the filter) and DeleteResourcesByExternalId (cleanup). CreateDeploymentInput has zero occurrences. Its framing was too narrow, though. DeploymentInfo.ExternalId's doc (types/types.go:418) reads "The unique ID for an external resource (for example, a CloudFormation stack ID) that is linked to this deployment" -- so the association is set by AWS-side integrations generally, not the CodePipeline output the issue named. The conclusion is unchanged: no public API call in this SDK can set it. That makes parsing the filter the right fix on its own, with no population path. Every deployment's ExternalID stays empty, so a non-empty filter correctly returns nothing -- which is what real AWS returns for a deployment no external resource is linked to. Silently dropping a modelled filter is the worse failure, because a client cannot distinguish "no matches" from "filter ignored". Adding the sixth condition pushed ListDeployments past gocognit's threshold (22 > 20). This repo bans gocognit/cyclop/funlen nolints, so the predicate is extracted into deploymentMatchesFilter rather than suppressed. The extraction is behaviour-preserving: same conditions in the same order, `continue` becoming `return false`. Snapshot format unaffected. Deployment is persisted directly via store.Table[Deployment], and ExternalID is additive with omitempty, so an older snapshot decodes it to "" -- which is the only value it can correctly hold. No codedeploySnapshotVersion bump, so no user snapshot is discarded. TestDeployments_ListExternalIDFilter covers both halves independently. Each neutered by line number, and each compiles and fails on its own: deployments.go:107 (the comparison) and handler_deployments.go:258 (threading the parsed field into the filter) both produce "[d-...]" should have 0 item(s), but has 1 Closes gopherstack-3qel Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…e dispatch-tracer gap Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ared allowlist gopherstack-oshm. Both doc comments claimed the two genericProtocolCodes lists were identical; errtargetaudit carried InternalServerException and errcodeaudit did not. The lists are now genuinely identical at 40 entries each, reconciled by REMOVING the entry rather than adding it. The evidence points that way, and it points hard. gopherstack-q9bs set the standard an entry must meet: it earns its place from the tool's brief, or from a service whose docs describe returning the code while its model declares no such shape -- which is how ValidationException survived, on kms's docs-2.json. Searching every vendored aws-sdk-go@v1.55.8 service model for that pattern finds zero hits for InternalServerException, against two for ValidationException. Meanwhile 51 of the pinned SDK's 166 modules declare it as a per-op typed exception, which is evidence of a modeled exception rather than a pre-dispatch protocol fault. The entry was also suppressing real findings. errcodeaudit goes 349 -> 352: services/forecast/handler.go:606 (confident -- single resolved module, absent from its deserializer set) and services/personalize/handler.go:230,268. Neither personalize@v1.50.4 nor personalizeruntime declares ANY server-fault type, so those three emissions are genuine mismatches that the allowlist was hiding. errtargetaudit's corpus is unchanged at 341/10/70, because gopherstack-udkm already made its lookup module-conditional -- the allowlist is consulted only where the service models the code nowhere, so removing an entry that no affected service reaches changes nothing there. A pre-existing test required the opposite and had to be replaced, not adjusted. TestGenericProtocolCodes_InternalServerException asserted the entry must be present, justified by "the 90-false-positive mgn case" -- a premise gopherstack-udkm had already disproved, since mgn@v1.48.4 declares InternalServerException in types/errors.go and in 3 deserializeOpError switches, making those emissions class A findings. The mgn concern was actually resolved by the module-conditional check, not by the allowlist entry. The replacement asserts absence and records why. TestScanServiceDir_PersonalizeInternalServerExceptionReported scans the real services/personalize tree and requires the findings to appear. Neutered by re-adding the entry to errcodeaudit's map: compiles, and fails with "personalize@v1.50.4 declares no server-fault type at all, so its InternalServerException emissions must be reported, not suppressed". Entries still resting on sibling-analogy rather than a citation, recorded but not changed: ThrottlingException, TooManyRequestsException, RequestLimitExceeded, AccessDeniedException, UnauthorizedException, ExpiredTokenException, and the InternalError/InternalServerError/ ServiceUnavailable/ServiceUnavailableException/ServerException/ ServiceException cluster. Closes gopherstack-oshm Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…lares
gopherstack-fm1e and gopherstack-l81f, worked as one cluster.
rds ModifyActivityStream emitted DBClusterNotFoundFault, which it does not
declare. Its declared set in rds@v1.124.1 is DBInstanceNotFound,
InvalidDBInstanceState and ResourceNotFoundFault.
The choice between DBInstanceNotFound and ResourceNotFoundFault is settled by
a contrast rather than by the op's own doc alone. StartActivityStream and
StopActivityStream BOTH declare DBClusterNotFoundFault; ModifyActivityStream
alone does not. AWS omitted it deliberately, which matches the op's
ResourceArn doc -- "The Amazon Resource Name (ARN) of the RDS for Oracle or
Microsoft SQL Server DB instance. For example,
arn:aws:rds:us-east-1:12345667890:db:my-orcl-db" -- an instance ARN with an
instance-shaped example. So the target is never ambiguous here, and the
generic ResourceNotFoundFault is not needed. DBInstanceNotFoundFault's own
doc is a word-for-word fit: "DBInstanceIdentifier doesn't refer to an
existing DB instance."
ErrInstanceNotFound already exists and already carries the right wire string,
so no sentinel edit was needed -- unlike the eventbridge and stepfunctions
fixes, where the sentinel's own errors.New literal named the invented code.
TestActivityStream_ClusterNotFound asserted DBClusterNotFound for all three
of Start, Stop and Modify. Correct for the first two, wrong for the third.
Converted to a per-case table that asserts the expected code AND two absent
codes for each op -- strictly more coverage than before, not a weakened
assertion. Neutered at activity_stream.go:76: compiles, and fails with the
response body showing DBClusterNotFoundFault where DBInstanceNotFound was
expected.
The lookup underneath is still cluster-scoped, so the emitted code and the
resolution now disagree. That needs activity-stream state modelled on
DBInstance, which is a model change rather than an error-code one; filed
separately rather than half-done here.
codedeploy's five delete and deregister ops are confirmed to have no safe
remedy, and no source changed. Each declared set was re-derived individually:
DeleteApplication, DeleteDeploymentGroup, DeleteDeploymentConfig and
DeregisterOnPremisesInstance declare only name-required, invalid-name,
invalid-role, in-use and invalid-operation codes -- no not-found among them,
and InvalidOperationException's doc ("An invalid operation was detected")
does not fit a missing resource. The live API reference pages carry only the
"HTTP 200 response with an empty HTTP body" boilerplate, which this campaign
has established is response-shape prose and NOT idempotency evidence --
codepipeline's DisableStageTransition carries it verbatim and still errors.
The existing landmine comments were already accurate and are left untouched;
PARITY.md records the independent re-derivation so a later pass does not
repeat it.
Closes gopherstack-fm1e
Closes gopherstack-l81f
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
gopherstack-sgbw. forecast resolved 6 of 63 ops and comprehend 27 of 85, not because either service is thin -- both dispatch every ground-truth op -- but because three dispatch shapes were invisible to the tracer. 115 implemented operations were being silently skipped. if/else-if chains. collectSwitchDispatchEntries walked only *ast.SwitchStmt, so forecast's `if action == "X"` chain (handler.go:131-153) was unseen. New collectIfChainDispatchEntries mirrors the switch collector. Index-assignment map population. The map-literal collector walked only *ast.CompositeLit, so comprehend's ops["X"] = ... statements (handler.go:266-307) were unseen even though its map IS func-typed. New collectIndexAssignDispatchEntries handles assignment-populated maps, including range loops over spec slices. Struct-valued dispatch maps behind a shared executor. isDispatchMapType required a func-typed map value, so forecast's map[string]operationSpec was invisible by construction -- and its keys never exist as literals, being built as "Create"+base inside addCRUD. collectPopulatorHelperKeys binds each call site's literal arguments and evaluates the concatenation; collectSharedExecutorFallback then binds the recovered keys to execute(). Two design calls worth recording. Concatenated keys are EVALUATED, not guessed: base is bound to a real literal at every call site, so this is substitution rather than convention. The cost is that conditionals inside the helper are ignored, yielding a few never-looked-up candidate keys, which is harmless because unmatched keys are never consulted. And the shared-executor fallback is deliberately scoped to the functions passed to service.HandleTarget, not the whole package, so ~50 recovered forecast keys cannot bind to an unrelated backend method that happens to share the comma-ok-lookup shape. findHandlersByNameFold was deliberately NOT widened. Its over-broad scan already produces accidental resolutions -- forecast's DeleteResourceTree resolves only because a backend method happens to share the op name -- and leaning on it would make coverage look better while meaning less. forecast 6/63 -> 63/63 comprehend 27/85 -> 85/85 Set-diff over (service, section, op, code, file:line) triples against a binary built from the pre-change tree: 465 before, 686 after, 0 removed, 221 added -- 217 forecast, 4 comprehend, and nothing in the other 158 services. Class A 335 -> 464; coverage warnings 70 -> 68 as forecast's and comprehend's implausible-resolution warnings clear. The forecast additions are the funnel shape this was expected to produce -- everything routes through execute() and its one-hop backend calls -- and the existing SHARED PLUMBING and ROLLUP tagging from gopherstack-2evc and gopherstack-s0dw picks them up correctly. Two comprehend additions are a real find rather than plumbing: CreateDataset and CreateEndpoint emit KmsKeyValidationException from the generic CreateResource, and while 14 comprehend ops declare that code, neither of those two does. All four mechanisms neutered individually by early return, each compiling and each failing its own test: the if-chain and index-assign collectors drop forecast to 61/63, index-assign drops comprehend to 27/85, and the populator helper drops forecast to 10/63. cloudwatchlogs, flagged suspect when this was filed, is confirmed unaffected at 119/230 both before and after -- a separate root cause, still open. Closes gopherstack-sgbw Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
gopherstack-k3ww and gopherstack-jyi3. k3ww turned on a version question the issue could not settle locally, since only kms@v1.55.4 was cached. Downloading v1.54.0 answers it: its deserializeOpErrorDescribeKey declares DependencyTimeoutException, InvalidArnException, KMSInternalException and NotFoundException -- byte identical to v1.55.4, with no InvalidGrantTokenException in either. Fifteen sibling ops DO declare it in both versions, and DescribeKey is in neither list. So this is not SDK drift: the 2026-07-12 PARITY.md entry that added grant-token validation here cited v1.54.0 as declaring the code, and that citation was simply wrong. The feature is therefore reverted, its test with it. DescribeKey no longer calls validateGrantTokenPresence. GrantTokens stays on the input struct because the field is real in both versions and must round-trip; it is just not validated, since DescribeKey's declared set gives it nothing to reject with. The helper remains in use by DeriveSharedSecret, Sign, Verify, GetPublicKey, GenerateMac and VerifyMac -- all six of which DO declare InvalidGrantTokenException -- so the revert is surgical rather than a removal of the mechanism. TestDescribeKey_GrantTokens_Validation asserted a bogus token is rejected and is replaced by TestDescribeKey_GrantTokens_NotValidated asserting it is accepted, with the version evidence recorded inline. The wire-shape test that pins GrantTokens round-tripping is untouched, so no coverage is lost -- only the assertion that a real client could receive a code AWS never sends. Neutered by re-adding the call before keyToMetadata: compiles, and fails with "InvalidGrantTokenException: grant token not found". jyi3 is comment-only. CreateGrant declares neither UnsupportedOperationException nor ValidationException, so gopherstack-5rjn's CreateKey remedy does not transfer -- there is no code to swap to, which is the gopherstack-hdvu rule holding again. But GrantOperation is a plain enum-constrained string shape with 17 values (api-2.json), a single-field structural constraint rather than the cross-field business rule that made CreateKey's KeySpec/KeyUsage pairing need its own declared code. That places it in gopherstack-q9bs's class, where ValidationException is a genuine pre-dispatch KMS wire fault, so ErrValidation is kept and the reasoning is now recorded at the site. The blanket 32-site sweep in 9052099 had removed the old landmine without leaving a per-site justification. Neither change touches grant evaluation or key authorization: jyi3 is enum membership only, and k3ww removes a check on a read-only metadata operation that makes no authorization decision. Full go test ./services/... run anyway given the surface: exit 0, no failures. Closes gopherstack-k3ww Closes gopherstack-jyi3 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…ields TestSnapshotVersionGuard was failing on this branch. Two backendSnapshot field additions landed without the golden being refreshed: Deployment.ExternalID services/codedeploy, commit 76ac705 Addon.Namespace services/eks, this branch The codedeploy one is mine. I accepted "additive with omitempty, no version bump needed" and committed it without running pkgs/persistence, so the branch has been red since. Caught while verifying the eks change, which hit the same guard. Both are genuinely additive and neither needs a version bump, which the guard itself says: "every old field is still present unchanged, so the diff is additive only". Confirmed by diffing the golden's version numbers before and after -update -- no service's version changed, and the only edits are the two inserted field lines. So no user snapshot is discarded, which is what a bump would have done. Refreshed with -update rather than hand-edited. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…e-system gopherstack-gala. CreateAddonInput.NamespaceConfig was unwired, so replaceAddonPodIdentityAssociationsLocked installed every addon-owned pod identity association into kube-system. Correct for the AWS-managed add-ons, wrong in general. The SDK supports the full round trip, so all three pieces are done rather than just the wiring. eks@v1.90.4: CreateAddonInput.NamespaceConfig *types.AddonNamespaceConfigRequest -- "If specified, this will override the default namespace for the addon." Addon.NamespaceConfig *AddonNamespaceConfigResponse (types.go:143), echoed on CreateAddon and DescribeAddon output. UpdateAddonInput has ZERO occurrences of the field -- namespace is immutable after creation on the real API, so the emulator ignores an inbound key there rather than accepting it. AddonInfo.DefaultNamespace (types.go:209) confirms the default is per-addon documentation rather than a universal constant, so kube-system is retained as the fallback when NamespaceConfig is absent -- correct for the add-ons this emulator lists, and what the existing tests encode. Snapshot handling is additive: Addon gains Namespace with omitempty, the registry marshals tables generically, and no eksSnapshotVersion bump is needed. The shared golden is refreshed in its own commit, since it also had to repair an earlier miss of mine in codedeploy. CreateAddon's backend signature gained a namespace parameter, so 15 pre-existing call sites across five test files pass "". Verified by diff that no assertion in any pre-existing test changed -- call-site churn only. Two guards neutered by line number, each compiling. addons.go:305 (the namespace selection) fails TestAddon_NamespaceConfig_PodIdentityAssociationUsesCustomNamespace with expected "efs-csi", actual "kube-system". handler_addons.go:160 (the wire parse) fails that test plus the round-trip and immutability tests. Two further tests pin the preserved kube-system default and pass unmodified. Not an authorization change: this selects which namespace an association is installed into, not how associations or trust policies resolve. Closes gopherstack-gala Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
…fects gopherstack-ejfu. Commit adbe691 made 129 findings visible in these two services for the first time. Triaged; none is a defect. Documentation only, no source changed. comprehend, 3 findings over 2 decision sites. store.go:230 and :233 are one shared KMS check applied twice inside CreateResource, the single constructor for all five resource types. It reads values["ModelKmsKeyId"] and values["VolumeKmsKeyId"], but comprehend@v1.43.4's CreateDatasetInput and CreateEndpointInput declare neither field -- CreateEndpoint's only mention of ModelKmsKeyId is prose inside DataAccessRoleArn's doc comment, not a member. So a real client can never populate them, the value is always "", and validateKmsKeyID("") returns nil by design. The check is correctly required for the other three resource specs, which do carry the fields. I filed this issue calling that pair "a real class A mismatch" on the strength of the declared-set diff alone -- 14 comprehend ops declare KmsKeyValidationException and these two do not. That much is true and is not the point: the guard cannot fire, so there is nothing to emit. This is the gopherstack-03rb shape, not a live mismatch. handler_detection.go:542 is unreachable for the same class of reason: BatchDetectDominantLanguage dispatches as h.batch(h.detectDominantLanguage, nil), and h.batch gates the language-code check behind `if allowedLanguages != nil`. Consistent with DetectDominantLanguage having no LanguageCode field at all. forecast, 217 rows over 127 distinct (op, code) pairs and 11 genuine sites. Every operationSpec.mode is a constant fixed at map-construction time, and h.execute switches on it to reach each backend method from exactly one call site. So the counts are arithmetic, not evidence: InvalidNextTokenException's 42 findings are exactly the non-list ops, ResourceAlreadyExistsException's 41 exactly the non-create ops, against an h.ops of 55 entries (create 14, describe 14, list 13, delete 13, update 1). The tracer treats every switch branch as reachable from every op. InvalidNextTokenException got the closest look, since a pagination guard firing on unpaginated ops would have been a real defect. It is not: the mode field genuinely gates it, and the 13 real List ops all declare it. That makes forecast a fourth variant of the no-defect shape, distinct from mgn's shared plumbing, ssm's idiomatic guards and cloudfront's absent wire field: unreachable-given-the-dispatch-table. Three of the 14 raw site rows are a tool artifact rather than a finding -- store.go:189-191 are Go builtin delete(map, key) calls resolved to the backend's same-named delete method. Filed separately as gopherstack-bfb3; measured at exactly 4 rows corpus-wide, all here. Closes gopherstack-ejfu Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
… entry gopherstack-pfyr. The finding itself was already settled as a class-1 false positive -- BatchGetCommitsError.errorCode is document data inside a 200 response, so it is not constrained by the operation's declared exception list, and BatchGetCommits declares neither commit-not-found code. The open question was whether the VALUE was right, and the issue recorded no evidence either way because AWS never enumerates that field's legal values. It still does not. The evidence turned out to be shape typing rather than doc text. In botocore's codecommit api-2.json: GetCommitInput.commitId shape ObjectId BatchGetCommitsError.commitId shape ObjectId CreateBranchInput.commitId shape CommitId ObjectId is a raw full-SHA lookup with no resolution step; CommitId is the specifier-resolution shape used by ops that accept a branch name or tag. So the per-entry field is typed identically to GetCommit's input and distinctly from the specifier ops. The declared sets line up with that split exactly. GetCommit is the ONLY operation in the module declaring CommitIdDoesNotExistException; eighteen others declare CommitDoesNotExistException, and every one of them takes a specifier. The doc texts say the same thing: "The specified commit ID does not exist" against "The specified commit does not exist or no commit was specified, and the specified repository has no default branch" -- that second clause is a default-branch fallback, which only makes sense for a specifier. So a BatchGetCommits entry for an unresolvable SHA carries GetCommit's code, not CreateBranch's. This is the same confusion gopherstack-8pe4 fixed on GetCommit itself, which is what the issue suspected. No pre-existing test asserted this field, so nothing was weakened. TestHandler_BatchGetCommits_ErrorCodeIsCommitIdDoesNotExist neutered at commits.go:188: compiles, and fails with expected "CommitIdDoesNotExistException", actual "CommitDoesNotExistException". Closes gopherstack-pfyr Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
gopherstack-bfb3. callExprEmissions matched a call's callee name against cls.Funcs, a name-keyed map built from any package FuncDecl returning only error -- methods included. services/forecast declares InMemoryBackend.delete, so every bare delete(map, key) inside that method's own body matched cls.Funcs["delete"] and was reported as an emission site attributing ResourceNotFoundException. The discriminator is a fact about Go rather than a heuristic, which matters because the name alone cannot decide it: forecast's real delete IS reached as a bare identifier from the dispatch table. A method can only be invoked through a selector or a method value, never as a bare unqualified identifier. So a bare call to a predeclared function name resolves to real package code only if the package declares a RECEIVER-LESS func of that name, which is the only way to shadow the builtin at package scope. bareBuiltinCall checks exactly that, using idx.Funcs and idx.Methods, which already carried the receiver split -- nothing new is computed. Selector calls are untouched, and the whole predeclared set is handled rather than just delete. Set-diff across all 160 services: 0 added, 42 removed, and every removed row is one of 14 ops at services/forecast/store.go:189, :190 or :191 -- the three builtin calls. Nothing else in the corpus changed. Headline counts stay at 464 class A, 10 orphan, 68 coverage warnings, because the phantom rows were extra evidence sites on findings that already existed rather than findings of their own, and forecast still resolves 63/63. The genuine attribution is preserved: handler.go:207 is h.Backend.delete(...), a selector call, and still reports. So does the real sentinel raise at store.go:181, still ROLLUP-tagged. Neutered by early-returning false from bareBuiltinCall: compiles, fails TestScan_BuiltinDeleteNotConstructorClassifier with expected 1 actual 3, and the corpus goes back to 4 delete-tagged rows from 1. Correcting my own filing: the issue said "exactly 4 rows corpus-wide, all the delete builtin". Three are phantom; the fourth, handler.go:207, is the real selector call and was always correctly attributed. The agent caught that and was right. Closes gopherstack-bfb3 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Per-service AWS parity audit of all 163 services, one service at a time, each
verified against the pinned
aws-sdk-go-v2source rather than from memory.What this is
Every service got an audit issue under epic
gopherstack-plq, covering fivedimensions: AWS behavior compliance, LocalStack parity, cross-service
integration, performance, and resource leaks. All 163 are closed.
Every confirmed bug carries its own bd issue and a regression test that was
proven to fail without its fix — each guard neutered individually by line
number, with the build confirmed still green so a compile error could not
masquerade as a passing proof.
Recurring bug classes
The sweep kept surfacing the same shapes, which is the useful output here:
row but not a side map keyed by the same identity, so a recreated resource
inherits the dead one's state. Worst case:
docdb'sDeleteDBClusterSnapshotleftsnapshotAttributesbehind, so a snapshotrecreated under a reused identifier inherited the previous one's
cross-account restore grants.
finding across the campaign.
(
vpclattice'sserviceArn), or a convenience behavior invented for anomitted required member (
ses's emptyPolicyNamesmeaning "returneverything").
(
sagemakerruntime'sBodyvsInputLocation), required members, andbidirectional field pairings (
transcribe'sShowSpeakerLabels/MaxSpeakerLabels).because
pkgs/store.Index.removeswaps the last element into the removedslot.
outposts:ListOutposts/ListSitesreturned thelive backend pointers
Table.Snapshothands out, then released the lockwhile the handler read them unlocked.
Verification
Every agent finding was re-derived independently before being committed: the
SDK citation re-read verbatim, the per-operation modeled error set extracted
directly from
deserializers.go, and the regression test re-run against aneutered guard. Several agent claims were corrected or rejected in the
process, and a few agent pushbacks against the brief were accepted as correct.
make bd-auditreports zero trailer mismatches and zero typo'd IDs across allcommits.
TestSnapshotVersionGuardis green and the persistence golden wasrefreshed only for additive field changes, never to silence a version bump.
Known limitations
available; agents reported this honestly rather than claiming clean. The
wire-shape and error-code work is the solid part.
PARITY.md's own documented convention —trust rows marked
okwhose files are unchanged sincelast_audit_commit—rather than re-deriving every operation.
unfixed, including permission boundaries never consulted in the IAM
enforcement path and
.syncstep-function tasks degrading tofire-and-forget. Those were scoped out of the audits, not resolved by them.
🤖 Generated with Claude Code
https://claude.ai/code/session_01HuRbN6tdkW27u2PFP46N1m