| service | codebuild | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sdk_module | aws-sdk-go-v2/service/codebuild@v1.72.4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| last_audit_commit | d522d763f | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| last_audit_date | 2026-10-09 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| overall | A | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ops |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| families |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| items_still_open | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| structural_gaps |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gaps | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deferred |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| leaks |
|
Audited the method-value goroutine launch site(s) here; added leak_main_test.go and go test -race -count=1 passes clean with no code change (false alarm).
Confirmed real: ImportSourceCredentialsInput.Username exists (pinned SDK
codebuild@v1.72.4/api_op_ImportSourceCredentials.go:57-59, *string, not
required -- doc comment: "The Bitbucket username when the authType is
BASIC_AUTH. This parameter is not valid for other types of source providers
or connections"). handler_source_credentials.go's
importSourceCredentialsInput parses it off the wire but
handleImportSourceCredentials never passes it to
InMemoryBackend.ImportSourceCredentials (source_credentials.go), and the
backend's SourceCredentials struct (models.go) has no field for it.
Not fixed, reclassified as a modelling gap rather than accept-and-drop data loss, for two reasons:
- No API surface can ever observe it either way. The real
SourceCredentialsInfo(the only type any codebuild op returns for stored credentials --types/types.go:2785-2802) has exactly four members (Arn/AuthType/Resource/ServerType) and noUsername. AWS itself never returns Bitbucket usernames back through any op; a black-box parity test cannot distinguish "gopherstack stores Username but never serializes it" from "gopherstack drops Username on the floor" -- there is noListSourceCredentials(or any other) response shape a real SDK client could assert against, so the round-trip proof the issue proposed is not constructible against the real API surface. - The existing design already discards the sibling required field the
same way.
ImportSourceCredentials'sToken-- the actual auth secret, always required -- is accepted and explicitly discarded (source_credentials.go:_ = token) for the identical reason: nothing in this emulator ever authenticates to a real Git host, and no op surfaces stored credential material back. Adding aUsernamefield that is stored but that no real op will ever read would be exactly the "fabricated/never-read field" anti-pattern this pass was warned against, not a fix for it.
No code changed for this issue.
Protocol: awsjson1.1 (single POST endpoint, X-Amz-Target: CodeBuild_20161006.<Op>).
Route matcher (RouteMatcher) is a simple X-Amz-Target prefix check — verified every
op in GetSupportedOperations() round-trips through doRequest's header-based dispatch
in the existing handler_test.go/codebuild_ops_test.go/aws_accuracy_test.go suites, not just
via Handler() direct calls that could bypass the matcher.
Timestamps (created, lastModified, startTime, endTime, etc.) are plain float64
Unix-seconds fields that marshal as bare JSON numbers — this is wire-compatible with the
real deserializer's case "created": ... jtv.(json.Number) -> smithytime.ParseEpochSeconds
path (confirmed by reading codebuild@v1.68.11/deserializers.go), even though the field
isn't typed via pkgs/awstime.Epoch. Not a bug; noted so a future auditor doesn't "fix" it.
Pagination: services/codebuild/pagination.go's paginateIDs applies nextToken/maxResults/
sortOrder uniformly across every List* op, using pkgs/page.New for the opaque-offset token
(matching the pattern already used by e.g. services/acm). defaultListPageSize = 100 matches
real AWS's documented page cap. ListProjects/ListFleets/ListReportGroups additionally
support sortBy (NAME|CREATED_TIME|LAST_MODIFIED_TIME) via dedicated
List*SortedBy(sortBy string) backend methods, since real AWS's request shape for exactly
these three ops includes a sortBy field (confirmed by field-diffing
api_op_ListProjects.go/api_op_ListFleets.go/api_op_ListReportGroups.go
against api_op_ListBuilds.go et al, which have no sortBy). ListBuildBatches(ForProject)/
ListReports(ForReportGroup) additionally support filter.status, matching real AWS's
BuildBatchFilter/ReportFilter request shapes (each a single optional status field).
TagResource / UntagResource / ListTagsForResource were gopherstack inventions, not real AWS
CodeBuild operations. Field-diffed against aws-sdk-go-v2/service/codebuild@v1.68.11: the
module directory has no api_op_TagResource.go, api_op_UntagResource.go, or
api_op_ListTagsForResource.go, and grepping every func (c *Client) ... across all
api_op_*.go files in the module yields exactly 59 operations — none of which are these three.
Real CodeBuild only exposes tagging inline via the tags field on CreateProject/
UpdateProject/CreateReportGroup/CreateFleet (cross-service ARN-based tag discovery is the
job of the separate resourcegroupstaggingapi service on real AWS, which CodeBuild does not
duplicate). Deleted tags.go (backend TagResource/UntagResource/ListTagsForResource
methods), handler_tags.go (the three op handlers), and tags_test.go (12 tests exercising the
invented API). Removed the three names from GetSupportedOperations()/dispatchTable() in
handler.go. TestHandler_GetSupportedOperations now asserts these three are absent, so a
future accidental reintroduction gets caught immediately. janitor_test.go's
TestJanitor_SweepCleansARNIndex (which used TagResource on an evicted build's ARN purely as
a convenient "does an ARN-based op see this as gone" probe) was rewritten to use BatchGetBuilds
instead — same assertion (no ghost row after eviction), real op.
-
Missing pagination wire shape on every List* operation (
services/codebuild/pagination.go,handler_projects.go,handler_builds.go,handler_build_batches.go,handler_reports.go,handler_fleets.go,handler_sandboxes.go). Every List* op previously accepted but silently ignorednextToken/sortBy/sortOrder/maxResults, always returning the full unpaginated result set withnextTokenalways omitted. A real client relying on the SDK paginator'sHasMorePages()(which checks the returnednextToken, not result-set size) would still terminate correctly, but a client testing "at least 100 items on this page then a token" load-testing/pagination-contract scenario, or a client usingsortOrder=DESCENDINGto see newest-first results, would silently get wrong data. Fixed by adding a sharedpaginateIDs(all []string, nextToken, sortOrder string, maxResults int32) (page.Page[string], error)helper (usingpkgs/page, matching the pattern inservices/acm) and wiring it into every List* handler.ListProjects/ListFleets/ListReportGroups(the three ops whose real request shape hassortBy) got newList*SortedBy(sortBy string)backend methods;ListFleets's default order was also corrected from ARN-string-ascending (an internal implementation artifact) to NAME-ascending, consistent withListProjects/ListReportGroupsand withsortBy=NAME. -
Projectmissing the top-levelsourceVersionfield (services/codebuild/models.go,projects.go,handler_projects.go). Real AWS'sProjectshape has asourceVersionfield distinct fromsecondarySourceVersions(confirmed viacodebuild@v1.68.11/types/types.go);CreateProjectInput/UpdateProjectInputalso carry it. Nothing threaded it through before — a client settingsourceVersiononCreateProjectwould silently have it dropped. Fixed by addingProject.SourceVersion,ProjectConfig.SourceVersion, and wiring it throughCreateProject/UpdateProject/applyProjectOptionalFields(update semantics: non-empty value overwrites, matching every other optional string field on this resource) and thecreateProjectInput/updateProjectInputwire structs. -
Webhookmissingstatus/statusMessage/manualCreation/lastModifiedSecret/secret/pullRequestBuildPolicy/scopeConfiguration(services/codebuild/models.go,webhooks.go,handler_webhooks.go). Field-diffed againstcodebuild@v1.68.11/types/types.go'sWebhookstruct. Since this emulator never performs a real GitHub/GitLab/Bitbucket round-trip,CreateWebhooknow synthesizes a terminalstatus: "ACTIVE"immediately (the state a client would eventually observe on real AWS after webhook provisioning completes) plus a generatedsecretandlastModifiedSecrettimestamp;manualCreation/scopeConfigurationare accepted onCreateWebhookand echoed back;pullRequestBuildPolicyis accepted on bothCreateWebhookandUpdateWebhook;UpdateWebhookalso gainedrotateSecret(regeneratessecret+ bumpslastModifiedSecretwhen true, leaves both untouched otherwise, matching real AWS'sUpdateWebhookInput.rotateSecretsemantics).
Covered by new table-driven tests: TestHandler_ListProjects_SortOrderDescending,
TestHandler_ListProjects_InvalidNextToken, TestHandler_ListProjects_SortByCreatedTime,
TestHandler_ListFleets_MaxResultsPagination, TestHandler_ListBuildBatches_FilterByStatus,
TestHandler_ListReports_FilterByStatus (pagination_test.go); TestHandler_Project_SourceVersion
(projects_test.go); TestHandler_CreateWebhook_ExtendedFields,
TestHandler_UpdateWebhook_RotateSecret (webhooks_test.go).
Prior-pass fixes (builds/build batches stuck IN_PROGRESS forever; Project.Webhook not mirrored
after CreateWebhook) remain in place and are covered by TestJanitor_AdvanceInProgressBuilds /
TestJanitor_AdvanceInProgressBuilds_LeavesTerminalBuildsAlone (janitor_test.go) and
TestHandler_Webhook_MirroredOnProject (webhooks_test.go).
Field-diffed Fleet/CreateFleetInput/UpdateFleetInput against
aws-sdk-go-v2/service/codebuild@v1.68.11/types/types.go and
awsAwsjson11_deserializeDocumentFleet directly (not against gopherstack's own
output, per parity-principles.md rule 2). Found and fixed a real, previously-unflagged
gap: Fleet had no id field at all (a real, separate field from name/arn on
types.Fleet), and CreateFleetInput/UpdateFleetInput's wire structs had no
overflowBehavior/imageId/fleetServiceRole members, so a real client setting any
of these had them silently dropped on create and had no way at all to change them
(or computeType/environmentType) after creation via UpdateFleet, which previously
only ever touched baseCapacity. Fixed by adding Fleet.ID/Fleet.ImageID, generating
a UUID id at CreateFleet time (mirroring how other resources in this service
generate IDs), and refactoring CreateFleet/UpdateFleet's backend signatures to take
CreateFleetOptions/UpdateFleetOptions structs (the growing flat-positional-parameter
lists were becoming unwieldy) wired through from new createFleetInput/
updateFleetInput JSON fields. UpdateFleet's "empty string leaves field unchanged"
semantics mirror the existing applyProjectOptionalFields convention for optional
string-field updates on this service.
STALE CLAIM, FIXED in the very next section (2026-07-25 pass #2 below) -- flagged by
cmd/staleclaims, gopherstack-anjf: this paragraph was dispatch-bait for a reader who
stopped at the first match. Original text follows, kept for history: "Also found, NOT
fixed in this pass": Fleet.ComputeConfiguration/ProxyConfiguration/
VpcConfig/ScalingConfiguration (all real fields on types.Fleet) remained entirely
unmodeled -- these are nested objects (attribute-based-compute vCPU/memory/disk specs,
subnet/security-group VPC config, scaling-type semantics) that would require real design
work, not a wire-shape passthrough fix. Documented as a new gaps: entry rather than
silently left unflagged like the id/overflowBehavior/imageId/fleetServiceRole gap was
before this pass. Closed in the second 2026-07-25 pass immediately below.
Covered by new tests: TestHandler_CreateFleet_ExtendedFields,
TestHandler_UpdateFleet_ExtendedFields (fleets_test.go).
Implemented ComputeConfiguration/ProxyConfiguration/VpcConfig/ScalingConfiguration
end to end, field-diffed against aws-sdk-go-v2/service/codebuild@v1.68.11's
types.ComputeConfiguration/types.ProxyConfiguration/types.VpcConfig/
types.FleetProxyRule/types.TargetTrackingScalingConfiguration/
types.ScalingConfigurationInput/types.ScalingConfigurationOutput directly (go doc
plus reading serializers.go's awsAwsjson11_serializeOpDocumentCreateFleetInput/
UpdateFleetInput for exact request field names, and deserializers.go's
awsAwsjson11_deserializeDocumentFleet for the response). Added new model types
(models.go): ComputeConfiguration, ProxyConfiguration, FleetProxyRule,
TargetTrackingScalingConfig; extended the existing (previously dead-field) ScalingConfiguration
type with TargetTrackingScalingConfigs. Fleet.VpcConfig deliberately reuses the
existing VpcConfig type already defined for Project -- the real
aws-sdk-go-v2/service/codebuild/types.VpcConfig used by both Fleet and Project has
the identical shape (securityGroupIds/subnets/vpcId), so a second, duplicate type
would have been pure duplication.
Wired through CreateFleetOptions/UpdateFleetOptions (fleets.go) -- extending the
existing options structs from the prior pass rather than inventing a parallel path, per
this pass's instructions -- and the createFleetInput/updateFleetInput JSON wire
structs (handler_fleets.go). UpdateFleet's nested-object semantics: a non-nil pointer
overwrites, nil (absent from the request) leaves the existing value unchanged, matching
real UpdateFleetInput's partial-update contract (distinct from the string fields' "empty
string leaves unchanged" convention, since a nested object has no equivalent "empty"
sentinel).
ScalingConfiguration.DesiredCapacity is response-only on real AWS (types. ScalingConfigurationOutput has it; types.ScalingConfigurationInput, the request shape,
does not) -- confirmed by diffing the two types separately in types/types.go. Since this
emulator does not model live auto-scaling telemetry, outputScalingConfiguration
(fleets.go) populates it with the fleet's baseCapacity on every Create/UpdateFleet
response, matching real AWS's own value immediately after a create/update, before any
scaling event has occurred -- not a fabricated number, the literal correct value for that
moment.
Disguised-stub pattern found and fixed while doing this: Fleet.ScalingConfiguration
already existed as a model field (with a real JSON tag) before this pass, but nothing
anywhere ever set it -- a stray leftover from an earlier partial attempt, matching the
"field exists, no write-sites" bug class documented in parity-principles.md. It is now
genuinely wired end to end.
Covered by a new table test, TestHandler_Fleet_NestedConfiguration (fleets_test.go,
cases: create_computeConfiguration, create_proxyConfiguration, create_vpcConfig,
create_scalingConfiguration_desiredCapacityMatchesBase,
update_overwrites_nested_configuration,
update_without_nested_configuration_leaves_it_unchanged).
Re-examined the items_still_open premise that DescribeCodeCoverages/DescribeTestCases/
GetReportGroupTrend "always return empty, blamed on no report-content ingestion pipeline."
Confirmed the content verdict is still correct (no build-artifact/report-content pipeline
exists to source real numbers from, and fabricating them would be worse than an empty
response per parity-principles.md) -- but the validation half of each op was checked
against aws-sdk-go-v2/service/codebuild@v1.72.4's botocore source
(codebuild/2016-10-06/service-2.json's per-operation errors list, which is the
authoritative declared-exception contract each op's real deserializer is generated from)
and two of the three had a real bug:
DescribeTestCases/GetReportGroupTrendaccepted a nonexistentreportArn/reportGroupArnand returned 200 with empty content. Real AWS declaresResourceNotFoundExceptionfor both ops. Fixed: both now look up the resource first and returnErrNotFoundif absent (reports.go'sDescribeTestCases/GetReportGroupTrend).DescribeCodeCoveragesdoes not declareResourceNotFoundException(onlyInvalidInputException) -- confirmed by reading the same errors list -- so its identical "accept anything, return empty" behavior was already correct and was left unchanged; a new test (describe_code_coverages_nonexistent_report_still_returns_empty) documents this asymmetry so a future pass doesn't "fix" it into a regression.GetReportGroupTrend'strendFieldwas parsed and never validated. Real AWS'sReportGroupTrendFieldTypeis a 9-value enum (types/enums.go:895-926); any string, including garbage, was silently accepted. Fixed:handler_reports.gonow checkstrendFieldagainst the real enum viaslices.Contains, rejecting withInvalidInputExceptionotherwise.reportArn/reportGroupArnwere never checked for presence on any of the three ops (nor wasGetReportGroupTrend'sreportGroupArn) -- all arerequiredmembers on the real input shapes. Fixed: added the standardin.X == ""→errInvalidRequestcheck already used throughout this file (seehandleDeleteReportetc.) to all three.GetReportGroupTrend's response was missingrawData, a real member ofGetReportGroupTrendOutput(api_op_GetReportGroupTrend.go) — the handler only ever returnedstats. Fixed:rawDatais now present as an empty list (structurally correct, not fabricated — there is no report data to populate it with).CodeCoverage/TestCasehad invented field names, not matchingaws-sdk-go-v2/service/codebuild@v1.72.4/types.CodeCoverage/types.TestCase(verified viadeserializers.go'sawsAwsjson11_deserializeDocumentCodeCoverage).CodeCoveragehad onlyfilePath/branchCoverage/lineCoverage; real AWS has 10 fields includingid/reportARN/lineCoveragePercentage/branchCoveragePercentage/linesCovered/linesMissed/branchesCovered/branchesMissed/expired.TestCasehad onlyname/status/duration; real AWS has 9 fields includingreportArn/testRawDataPath/prefix/durationInNanoSeconds/message/testSuiteName/expired(nodurationfield exists on the real type at all). Fixed both models to match;handler_reports.gonow marshals the real slices directly instead of hand-buildingmap[string]anywith the wrong keys. Since both lists remain always-empty (per the content verdict above), this has no observable effect today, but is now correct for whenever report-content ingestion is eventually implemented.
Deliberately not implemented: DescribeCodeCoverages's sortBy/sortOrder/maxResults/
nextToken/minLineCoveragePercentage/maxLineCoveragePercentage and DescribeTestCases's
filter/maxResults/nextToken request fields. Unlike the pagination/filter parameters
fixed on ListReports/ListBuildBatches/etc in the 2026-07-23 pass (which silently returned
wrong results against real, non-empty data), these parameters would have zero observable
effect: the result set they'd sort/filter/paginate is provably always empty (see above), so
accepting-and-ignoring them is behaviorally identical to not accepting them at all. Revisit
this decision if/when report-content ingestion is ever implemented.
Swept the rest of the service for the same two bug classes (nonexistent-resource accepted; hand-written checks vs the real error contract) and found:
- The inverse bug in five
Delete*ops.DeleteProject/DeleteBuildBatch/DeleteReport/DeleteReportGroup/DeleteFleetall rejected a nonexistent resource withResourceNotFoundException(400) — but real AWS declares no such exception for any of the five (DeleteProject.errors/DeleteBuildBatch.errors/DeleteReport.errors/DeleteReportGroup.errors/DeleteFleet.errors: all just["InvalidInputException"]), meaning all five are idempotent deletes on real AWS. This matches the precedent already established (and already correctly implemented) byDeleteResourcePolicy("idempotent, matches AWS" inops:above). Fixed all five to no-op on a missing resource instead of erroring. Cross-checked the twoDelete*ops that legitimately keep erroring —DeleteWebhook/DeleteSourceCredentials— both do declareResourceNotFoundExceptionfor real, so they were correctly left unchanged. ListReportsForReportGrouphad the same missing-existence-check bug asDescribeTestCases/GetReportGroupTrendabove: a nonexistentreportGroupArnreturned an empty list instead ofResourceNotFoundException(real AWS declares it for this op, confirmed via the same botocore errors list — unlikeListReports/ListReportGroups, which correctly don't and were correctly left alone). Fixed the same way: existence check before listing,ListReportsForReportGroup's backend signature gained anerrorreturn (no external call sites outsideservices/codebuild, confirmed by repo-wide grep;go vet .clean at the repository root).
New/updated tests: reports_test.go (TestCodeBuild_ReportExtras,
TestCodeBuild_Reports, TestCodeBuild_ReportGroups), fleets_test.go
(delete_missing_fleet_is_idempotent), build_batches_test.go
(delete_missing_is_idempotent), projects_test.go (not_found_is_idempotent),
handler_test.go (TestHandler_ErrorTypeMapping's delete_project_missing_is_idempotent/
delete_fleet_missing_is_idempotent), pagination_test.go/persistence_test.go (updated to
create real report groups instead of hand-constructing ARNs that were never registered, which
the new ListReportsForReportGroup existence check would otherwise correctly reject).
2026-08-22 (gopherstack-r80d, batch 34 -- required-OUTPUT-member sweep, wrapped-type-shape
candidate, 0 bugs): every codebuild op's <Op>Output declares zero required members at its
own top level (confirmed via cmd/requiredoutputfields), invisible to every ranking this
campaign used through batch 33. Selected as one of the two candidates named by batch 33's
"ops with zero required fields wrapping richly-required domain types" mechanism test (services/ _REQUIRED_OUTPUT_CANDIDATES.md's batch-33 section) and given the full hand audit that batch
left undone.
Walked every non-slice field of every <Op>Output one hop into its own type
(aws-sdk-go-v2/service/codebuild@v1.72.4/types/types.go); the only wrapped types declaring
=2 required members of their own are
ProjectEnvironment(ComputeType,Image,Type--Project/Build/BuildBatch/Sandboxeach nest it via a field namedEnvironment) andScopeConfiguration(Name,Scope-- nested one hop insideWebhook). Of those, onlyImage(*string) andName(*string) are provable per this campaign's pointer-vs-enum rule;ComputeType/Type/Scopeare non-pointer enums and not provable regardless of gopherstack's behavior.
All confirmed correctly emitted, no bugs:
EnvironmentonBuild/BuildBatch/Sandbox(*ProjectEnvironment,omitempty) is always populated in practice:StartBuild(builds.go) copies it from the project viaapplyBuildOverrides/env, never leaves it nil.ProjectEnvironment.Imagehas noomitemptytag in gopherstack's own wire struct (models.go), so even a hypothetically empty value would still serialize the key.ScopeConfigurationonWebhookis not itself Smithy-required (only present for GitHub/ GitHub Enterprise org-scoped webhooks, matching real AWS's documented restriction) and is threaded straight through fromCreateWebhookInput.ScopeConfigurationto the response with no fabrication or dropping (handler_webhooks.go/webhooks.go);Name/Scopein gopherstack'sScopeConfigurationstruct carry noomitempty, so whatever a real client's own required-field validator already guaranteed on the way in survives to the response unmodified.- One hop further:
ProjectSourceVersion(SourceIdentifier,SourceVersion, both*stringand provable, nested inProject/Build'sSecondarySourceVersionsslice) is passed through verbatim from the request with noomitemptyon either field in gopherstack's struct -- clean, same reasoning asScopeConfiguration. - Also checked one hop further into
Fleet/ReportGroup/CommandExecution/Sandbox's other nested types (ComputeConfiguration,ProxyConfiguration,ScalingConfigurationOutput,FleetStatus,LogsLocation,SandboxSession,LogsConfig,VpcConfig,ProjectSource): all declare zero required members of their own exceptProjectSource.Type(SourceType, a non-pointer enum) -- not provable, not pursued further.
Wrapped-type-shape hypothesis verdict for codebuild: did not hold. Every candidate this
mechanism surfaced was either already correctly wired or not provable under this campaign's own
rules. See services/_REQUIRED_OUTPUT_CANDIDATES.md's batch-34 section for the cross-service
verdict (the paired candidate, fsx, did find a bug this way).
An existing wire_field_fixes_test.go (1 test, StartBuild inheriting
Cache/VpcConfig/FileSystemLocations) marked this service PARTIAL, not
finished, per this campaign's own established rule. Ran the write-only-state
method first: for every write op (CreateProject/UpdateProject/CreateFleet/
UpdateFleet/CreateWebhook/UpdateWebhook/PutResourcePolicy/StartBuild/
StartSandbox/StartCommandExecution/RetryBuild), field-diffed the real
aws-sdk-go-v2/service/codebuild@v1.72.4 request/response types directly
against gopherstack's wire structs and backend models (never against
gopherstack's own prior output). All 7 bugs found this way; none via a
key-diff pass alone (every key gopherstack already emitted was correctly
named).
-
Project.BadgeEnabled/Badge— accepted from the request, never stored at all.CreateProjectInput/UpdateProjectInput.BadgeEnabled *boolis real (api_op_CreateProject.go:65,api_op_UpdateProject.go:48;types.ProjectBadge{BadgeEnabled, BadgeRequestUrl}on the response,deserializers.go:11042's"badge"case), but gopherstack'sprojectConfigFieldswire struct had nobadgeEnabledfield at all — the value never reached the backend. Fixed:projectConfigFields,ProjectConfig.BadgeEnabled *bool,InMemoryBackend.applyBadge(projects.go) generates a stable synthesizedbadgeRequestUrlthe first time badging is enabled, matching real AWS not rotating it on every subsequentUpdateProject. -
ProjectSourcemissingbuildStatusConfig/gitSubmodulesConfigentirely. Both are real fields ontypes.ProjectSource(serializers.go:4299,4311;deserializers.go:12083,12101) affectingSource/SecondarySourcesonCreateProject/UpdateProjectand (by inheritance)Build.Source. Silently dropped since gopherstack'sProjectSourcemodel had neither field. Fixed: addedBuildStatusConfig/GitSubmodulesConfigtypes and fields toProjectSource(models.go); flows through automatically via the existingSource *ProjectSourcerequest/response wiring, no handler changes needed. -
ProjectEnvironmentmissingcomputeConfiguration/dockerServer/fleet/hostKernelentirely. All four are real fields ontypes.ProjectEnvironment(deserializers.go:10075,11705,11719,11729, 11734). Most notablyfleet(types.ProjectFleet{FleetArn}) — the field that assigns a project to a reserved-capacity compute fleet, the exact feature this service's own Fleet API already models end to end — was silently discarded on everyCreate/UpdateProject. Fixed: addedComputeConfiguration/DockerServer/DockerServerStatus/ProjectFleettypes and the four fields toProjectEnvironment(models.go). -
StartBuild'ssourceVersionoverride corruptedSource.Location. Realtypes.Buildhas aSourceVersion *stringfield distinct from bothSource.LocationandResolvedSourceVersion(types/types.go'sBuildstruct) — the requested commit/branch/tag to build, not the source URL. Gopherstack'sBuildmodel had noSourceVersionfield at all, andapplyBuildOverrideswrote the version string directly intosrc.Location, corrupting the project's real source URL on every build that set a sourceVersion. Fixed: addedBuild.SourceVersion;StartBuildnow sets it (and a best-effortResolvedSourceVersion, mirroring the requested version since this emulator does no real git resolution) without touchingSource.Location. -
StartBuildInput.ArtifactsOverride— parsed off the wire, then silently dropped before reaching the backend.handler_builds.go'sstartBuildInputalready declaredArtifactsOverride *ProjectArtifactswith a correct JSON tag, buthandleStartBuildnever forwarded it intoStartBuildConfig— a textbook accept-then-drop bug. Swept the rest ofStartBuildInput(api_op_StartBuild.go) against gopherstack's handler and found ~20 more real override fields entirely unmodeled (cacheOverride,registryCredentialOverride,fleetOverride,sourceAuthOverride,buildStatusConfigOverride,gitSubmodulesConfigOverride,insecureSslOverride,reportBuildStatusOverride,privilegedModeOverride,gitCloneDepthOverride,sourceTypeOverride,sourceLocationOverride,environmentTypeOverride,certificateOverride,imagePullCredentialsTypeOverride,hostKernelOverride,encryptionKeyOverride,secondaryArtifactsOverride,secondarySourcesOverride,secondarySourcesVersionOverride,queuedTimeoutInMinutesOverride,autoRetryLimitOverride). Fixed: all now accepted and applied (builds.go'sapplySourceOverrides/applyEnvironmentOverrides/applyEnvironmentScalarOverrides/applyBuildOverrides,handler_builds.go).idempotencyTokenandlogsConfigOverrideare deliberately still not modeled: neither has any observable effect through a real read op (this emulator doesn't deduplicate submissions, andBuildhas nologsConfigfield of its own —Build.Logs, the actual log-delivery-location field, isn't populated by this emulator regardless, since no real log delivery is simulated). -
Build/no model support forAutoRetryConfigat all. Realtypes.Build.AutoRetryConfig *types.AutoRetryConfig{AutoRetryLimit, AutoRetryNumber, NextAutoRetry, PreviousAutoRetry}lets a client detect its own retry chain — a documented real use ofRetryBuild. Gopherstack modeled neither the field nor the chain. Fixed: addedAutoRetryConfigtoBuild;StartBuildsetsAutoRetryLimitfrom the project (orautoRetryLimitOverride) withAutoRetryNumber: 0;RetryBuildincrementsAutoRetryNumber, setsPreviousAutoRetryto the original build's ARN, and (mutating the still-live in-store original) sets the original'sNextAutoRetryto the new build's ARN. -
StartSandboxnever inherited any project configuration. Realtypes.Sandboxcarries the identical project-derived field set astypes.Build(environment/source/vpcConfig/serviceRole/encryptionKey/sourceVersion/secondarySources/secondarySourceVersions/fileSystemLocations/timeoutInMinutes/queuedTimeoutInMinutes— confirmed viaawsAwsjson11_deserializeDocumentSandbox), but gopherstack'sSandboxmodel only ever hadid/arn/projectName/status/startTime/endTime—StartSandboxcreated a sandbox with none of a real project's configuration attached. Fixed: added the matching fields toSandboxand wiredStartSandboxto copy them from the project, the same wayStartBuildalready does forBuild.currentSessionandlogConfigdeliberately not modeled:StartSandboxConnectionalready documents (see itsops:row above) that a real interactive terminal isn't simulated, andlogConfighas the identical no-observable-effect reasoning asStartBuildConfig'sLogsConfigOverrideabove.
Also fixed while sweeping sandbox command execution wire shapes:
CommandExecution.ExitCodewasint32; real wire type isstring.deserializers.go:9084's"exitCode"case requires a JSON string ("expected NonEmptyString to be of type string") — a real client's decoder would reject a numeric value outright. Never actually triggered pre-fix because the field was never populated (zero value +omitemptyomits the key), but a latent hard-decode-error landmine per this campaign's failure-signature #2. Fixed: retyped tostring,StartCommandExecutionnow sets it to"0"(the emulator always completes commands synchronously and successfully).CommandExecution's stderr field used the wrong wire key. Gopherstack emittedstandardErrorContent; real AWS's key isstandardErrContent(deserializers.go:9125) — a silent drop, a real client'sStandardErrContentwas always nil. Fixed: renamed the field and its JSON tag (StandardErrContent).
Not reached this pass (documented, not fabricated as covered):
ImportSourceCredentials, DeleteSourceCredentials, ListSourceCredentials,
InvalidateProjectCache, UpdateProjectVisibility,
ListCuratedEnvironmentImages, ListSharedProjects/ListSharedReportGroups,
DescribeCodeCoverages/DescribeTestCases/GetReportGroupTrend (re-verified
still correctly empty-content per the 2026-08-11 pass, not re-audited further),
all List* pagination paths (unchanged this pass), StartSandboxConnection
(already documented partial, unchanged). enumcheck (go run ./cmd/enumcheck) reports 0 findings for codebuild.
Round-trip tests (wire_field_fixes_test.go, all driving the real
aws-sdk-go-v2/service/codebuild client against this handler): each of the 9
bugs above has a dedicated *_RealClient test; each was hand-verified to
fail against the pre-fix code (via git stash of only the fix files, never
the test file) and pass after.
Gates: go build, go vet, go test -race -count=1, golangci-lint run —
all clean (./services/codebuild/...).
Census: paginateIDs and paginateCommandExecutions (pagination.go) both delegate
straight to pkgs/page.New after an optional descending-order reversal — an offset
token pkgs/page itself clamps to the collection length. No equality-scan cursor
anywhere in this service. Re-checked every List* handler for a bypass of the shared
paginator (per this campaign's specific warning that ListCommandExecutionsForSandbox
had one, fixed 2026-08-29 in 4cc1b6238/an adjacent commit on this same branch, already
reflected above as paginateCommandExecutions): every remaining List* op either calls
paginateIDs/paginateCommandExecutions, or has no real-AWS pagination fields at all
(ListSharedProjects, ListSharedReportGroups, ListSourceCredentials,
ListCuratedEnvironmentImages, all BatchGet*). No further bypasses found. Verdict:
correct, no bug found.
Added pagination_arithmetic_test.go: a real aws-sdk-go-v2 typed-client boundary walk
over ListProjects (N=7, page implicit default, assert.ElementsMatch against the full
set).
Gates: go build, go vet, go test -race -count=1, golangci-lint run — all clean
(./services/codebuild/...).
Dispatched by targeting ("no filter_default_semantics covledger row"); covledger -service codebuild
in fact credits only request_field_never_read (0c9b33a27) and wrong_wire_key (e50f52dce) — a
different labeling of what four prior passes on this branch (e50f52dce, 4cc1b6238, 0c9b33a27, plus
the original wire_field_fixes_test.go sweep) already substantially covered as wire-key, request-field,
pagination-cursor, and first-element-only-list bugs. Read all four commits' full diffs and PARITY notes
before doing new work, per this campaign's "twice already" precedent for a service the ledger calls
unaudited that was in fact already audited.
The brief's own lead (documented sortBy/sortOrder defaults) does not hold for codebuild. Checked
every List* operation's own doc comment in the pinned SDK (api_op_ListBuilds.go,
ListBuildsForProject, ListBuildBatches(ForProject), ListProjects, ListReportGroups, ListReports (ForReportGroup), ListSandboxes(ForProject), ListCommandExecutionsForSandbox, ListFleets,
ListSharedProjects, ListSharedReportGroups) plus the live AWS API Reference pages for ListBuilds,
ListProjects, and ListReportGroups (3 pages fetched, all three carried the injected "aws
agent-toolkit search-skills" footer, treated as data and ignored) — none document a default sort order or
a default sortBy criterion. Correctly recorded as documentation being SILENT, not a bug: gopherstack's
paginateIDs/paginateCommandExecutions (pagination.go) treat omitted sortOrder as ascending and
omitted sortBy as name-ascending (ListFleetsSortedBy/ListProjectsSortedBy/ListReportGroupsSortedBy
switch defaults), which is a reasonable convention but not something the doc contradicts either way.
Swept every genuine filter-typed field in the service (not just sortBy/sortOrder scalars), all correctly implemented, re-verified from source:
ListReportsInput.Filter/ListReportsForReportGroupInput.Filter(types.ReportFilter{Status}) —handler_reports.go'sreportFilter{Status string}correctly decodes the nested{"filter": {"status": ...}}wire shape (not a flat field), compared by exact equality againstReport.Statusinreports.go'sListReports/ListReportsForReportGroup, matching the doc's "You can filter using one status only."ListBuildBatchesInput.Filter/ListBuildBatchesForProjectInput.Filter(types.BuildBatchFilter{Status}) — same nested-object decode, compared againstBuildBatch.BuildBatchStatus, matching "Only batch builds that have this status will be retrieved."ListFleetsInput.SortBy(CREATED_TIME|LAST_MODIFIED_TIME|NAME) —ListFleetsSortedByimplements all three (NAME is the natural construction order, the other two sort explicitly).DescribeTestCasesInput.Filter(types.TestCaseFilter{Keyword,Status}) andDescribeCodeCoveragesInput.{MinLineCoveragePercentage,MaxLineCoveragePercentage,SortBy,SortOrder}are provably inert, not merely unimplemented: grepped the whole package forTestCase{/CodeCoverage{construction sites — the only ones are the tworeturn []TestCase{}, nil/return []CodeCoverage{}, nilunconditional-empty returns inreports.go. No write path anywhere populates either type (matchesitems_still_openabove, already correctly recorded as a content gap, re-confirmed rather than re-derived from the note alone). No legal filter value can change an always-empty result — same reasoning as this campaign's other "provably inert" retirements — soDescribeTestCasesInput.Filterbeing undeclared indescribeTestCasesInputis the never-declared axis, not a fixable value-semantics bug, and is correctly left alone.
One new gap found and recorded (not fixed): ListBuildsForProjectInput.SortOrder's own doc states
setting it on a project with more than 100 builds must error; handleListBuildsForProject never checks
build count before sorting. Missing rejection — validation axis, kept separate from this pass's
value-semantics remit per the campaign's own discipline; see gaps: above.
Strengthened coverage rather than fixed a bug: the confirmed first-element-only-list shape doesn't
apply anywhere in codebuild's current filter surface — every real Filter type here (ReportFilter,
BuildBatchFilter, TestCaseFilter) carries a single-value Status/Keyword scalar, not a Values []string list, so no test addition was needed for that specific blind spot (unlike fsx, same pass,
services/fsx/PARITY.md). No code or test changes made to this service this pass.
Gates: go build ./services/codebuild/..., go vet ./... (repo-wide, clean), go test -race -count=1 ./services/codebuild/..., golangci-lint run ./services/codebuild/... (0 issues).
2026-09-04 parity sweep: 2 bugs found and fixed (DeleteProject cascade, ListBuildsForProject sort-order limit)
Ran the standard mechanical checks (sentinel round-trip, parsed-then-dropped fields,
lifecycle preconditions, ghost rows after delete, fabricated/unreachable error codes)
across the whole package before pursuing leads. Baseline before any edit: go build
clean, golangci-lint run ./services/codebuild/... reported 0 issues.
Bug 1 — DeleteProject cascade-deleted a project's builds; real AWS does not.
api_op_DeleteProject.go (pinned codebuild@v1.72.4): "Deletes a build project. When
you delete a project, its builds are not deleted." projects.go's DeleteProject
walked the buildsByProject index and called b.builds.Delete(id) for every build of
the deleted project — the opposite of the documented behavior. This file's own prior
DeleteProject PARITY note had (incorrectly) described the cascade as a deliberate
cleanup rather than flagging it, and janitor_test.go's
TestDeleteProject_CleanupBuilds asserted the cascade outright — a case of a test
encoding the bug, called out per this campaign's own instructions rather than quietly
rewritten: the test is now TestDeleteProject_DoesNotCleanupBuilds and asserts builds
survive project deletion (BuildCount/BuildARNIndexSize/BuildsByProjectSize all stay
at 2, not 0). Fix: DeleteProject now only calls b.projects.Delete(name); idempotency
on a nonexistent name is preserved because store.Table.Delete is a safe no-op on a
missing key. DeleteProject models only InvalidInputException
(botocore codebuild/2016-10-06/service-2.json operations.DeleteProject.errors) so no
error-path change was needed.
Fail-before: neutered by re-inserting the cascade-delete loop under if true {} in
DeleteProject (verified by printing the function body post-edit); rerunning
TestDeleteProject_DoesNotCleanupBuilds failed with:
Not equal: expected: 2 actual: 0 on all three assertions (BuildCount,
BuildARNIndexSize, BuildsByProjectSize). Restored from a scratchpad copy and reverified
0 issues / all package tests green.
Bug 2 — ListBuildsForProject didn't enforce its own documented sortOrder limit.
api_op_ListBuildsForProject.go's SortOrder field doc: "If the project has more than
100 builds, setting the sort order will result in an error." (Already correctly
identified as an open gap by the 2026-08-31 gopherstack-uox6 pass but left unfixed.)
handleListBuildsForProject (handler_builds.go) called paginateIDs with whatever
sortOrder the client sent regardless of how many builds the project had. Fix: added a
check right after fetching the project's build IDs — sortOrder != "" && len(ids) > defaultListPageSize now returns %w: sortOrder cannot be set... wrapping
ErrValidation (→ InvalidInputException, matching the only error
ListBuildsForProject models).
New test TestHandler_ListBuildsForProject_SortOrderBuildCountLimit
(pagination_test.go) covers three cases: 101 builds + sortOrder → 400; 100 builds +
sortOrder → 200 (boundary); 101 builds + no sortOrder → 200 (unaffected). Fail-before:
neutered the guard's condition to if false && ... (verified by printing the line
post-edit and confirming the build still compiled); only the
over_100_builds_rejects_sort_order subtest failed, with Not equal: expected: 400 actual: 200 — the other two subtests correctly still passed, proving the guard's single
condition is exactly what the test exercises. Restored from a scratchpad copy and
reverified 0 issues / all package tests green.
Other mechanical checks, no further bugs found: sentinel round-trip (all three
package sentinels — ResourceNotFoundException/ResourceAlreadyExistsException/
InvalidInputException — are both defined and reachable via handleError; no handler
returns any of the other three real CodeBuild error types
(AccountLimitExceededException/OAuthProviderException/AccountSuspendedException),
which is correct since none of those conditions (account-level resource limits, OAuth
provider handshake failures, account suspension) are modeled anywhere in this emulator
and fabricating them would be worse than omitting them. StatusType's FAILED/FAULT/
TIMED_OUT values are declared but structurally unreachable — the janitor's
advanceInProgressBuilds only ever advances IN_PROGRESS builds to SUCCEEDED,
because no build-execution/failure semantics exist in this emulator to source a real
failure from; this is the same category of structural gap as the already-documented
items_still_open report-content entries, not a fixable per-op bug, and is left
unfixed here for the same reason. BuildBatch's deeper lifecycle gaps are the
previously-identified unmodelled-capability structural gap (full batch group/environment
lifecycle design), not touched this pass.
Gates: go build ./services/codebuild/..., go test -race -count=1 ./services/codebuild/..., golangci-lint run ./services/codebuild/... (0 issues before
and after). Cross-service check: services/cloudformation/resources_codebuild.go calls
InMemoryBackend.DeleteProject with an unchanged signature — go test -race -count=1 . ./services/cloudformation/... passes.
gopherstack-9ckk recorded that BuildBatch carried a small fraction of the real
types.BuildBatch shape (7 fields: id/arn/projectName/status/tags/startTime/endTime) —
unmodeled capability, not a field-copying fix, per the bd note ("no sibling here quietly
getting it right, and no existing state to surface"). This pass modeled it the way AWS
models it, confirmed member-by-member against the pinned SDK
(aws-sdk-go-v2/service/codebuild@v1.72.4).
The AWS model, cited. types.BuildBatch (types/types.go:302): Arn, Artifacts *BuildArtifacts (gopherstack reuses *ProjectArtifacts for this, the same
simplification Build.Artifacts already makes — see that field's own doc comment),
BuildBatchConfig *ProjectBuildBatchConfig (types/types.go:1707: BatchReportMode,
CombineArtifacts, Restrictions *BatchRestrictions, ServiceRole, TimeoutInMins),
BuildBatchNumber, BuildBatchStatus StatusType (types/enums.go:1127:
SUCCEEDED|FAILED|FAULT|TIMED_OUT|IN_PROGRESS|STOPPED), BuildGroups []BuildGroup
(types/types.go:519), BuildTimeoutInMinutes, Cache, Complete, CurrentPhase,
DebugSessionEnabled, EncryptionKey, EndTime, Environment *ProjectEnvironment,
FileSystemLocations, Id, Initiator, LogConfig *LogsConfig, Phases []BuildBatchPhase (types/types.go:464: Contexts, DurationInSeconds, EndTime,
PhaseStatus StatusType, PhaseType BuildBatchPhaseType, StartTime), ProjectName,
QueuedTimeoutInMinutes, ReportArns, ResolvedSourceVersion, SecondaryArtifacts,
SecondarySourceVersions, SecondarySources, ServiceRole, Source *ProjectSource,
SourceVersion, StartTime, VpcConfig. BuildGroup (types/types.go:519):
CurrentBuildSummary *BuildSummary, DependsOn []string, Identifier,
IgnoreFailure, PriorBuildSummaryList []BuildSummary. BuildSummary
(types/types.go:650): Arn, BuildStatus StatusType, PrimaryArtifact *ResolvedArtifact (types/types.go:2468: Identifier, Location, Type),
RequestedOn, SecondaryArtifacts []ResolvedArtifact. Build.BuildBatchArn
(types/types.go:67) links a child build back to its batch.
StartBuildBatchInput (api_op_StartBuildBatch.go:28) carries the *Override fields
gopherstack's StartBuildBatchConfig mirrors 1:1 (see its own doc comment for the three
fields it deliberately omits: IdempotencyToken, LogsConfigOverride, and the
FleetOverride/HostKernelOverride/AutoRetryLimitOverride StartBuildInput has that
StartBuildBatchInput simply doesn't declare). This confirms the issue's own framing:
a batch's Environment/Source/Artifacts/Cache default to the project's and can
diverge via these overrides, exactly like StartBuild already does for a plain build —
that divergence is now real, not just theoretically possible.
Batch definition parsing (batchspec.go, new file): the buildspec batch: section
is now actually parsed (YAML, gopkg.in/yaml.v3, already a direct dependency elsewhere
in this repo — cloudformation/template.go, apigateway/import.go, etc.). build-list
(flat, no dependencies) and build-graph (depend-on ordering, cycle-checked) are
fully implemented, including each node's own buildspec/env (compute-type, image,
type, privileged-mode, variables) overrides, layered onto the batch's own environment.
build-matrix is expanded (static env layered under the cross product of dynamic
image/compute-type/type/privileged-mode/variables lists, max 100 builds); build-fanout isn't a real CodeBuild
buildspec keyword (confirmed: AWS's own "Batch build buildspec reference" documents
exactly build-list/build-matrix/build-graph) so it was not modeled. A buildspec
with no batch: section at all returns the real error: InvalidInputException (ErrValidation) —
verified as the right exception type (StartBuildBatch's deserializers.go handler
is the same generic awsAwsjson11_deserializeOpErrorStartBuildBatch shape used by every
op, driven entirely by the server's runtime __type/message, not a per-op enumerated
list — AWS doesn't publish the exact server-side message text in the client SDK, so
errNoBatchConfig's message string is descriptive, not a verified literal, same
disclosure this pass makes explicit in the code comment next to it).
How children are spawned and dependencies ordered. Builds in this emulator don't
complete synchronously inside StartBuild — they stay IN_PROGRESS until the
Janitor's tick advances them (janitor.go, pre-existing). So a build-graph's
dependency ordering rides that same mechanism rather than anything new:
StartBuildBatch starts every group with no unmet dependencies immediately
(startBatchChildBuild, a real Build with BuildBatchArn set, sharing the batch's
resolved environment); a group with dependencies is left with no CurrentBuildSummary
until they resolve. reconcileBatch (called both at StartBuildBatch/
RetryBuildBatch and, critically, from the Janitor's advanceBuildsLocked after it
flips a tick's IN_PROGRESS builds to SUCCEEDED) refreshes each started group's
summary from its live child, starts any now-eligible group, and derives the batch's
overall status. No extra backend-only scheduling state was needed for this: BuildGroup
already carries Identifier/DependsOn/IgnoreFailure/CurrentBuildSummary, which is
enough to resume purely from the persisted BuildBatch record (see snapshot decision
below) — restart-safe by construction, not by extra bookkeeping.
How batch status derives. deriveBatchStatus (build_batches.go) matches the
issue's own framing: IN_PROGRESS while any group is neither terminal nor permanently
blocked, SUCCEEDED once every group has (or ignored its own failure via
IgnoreFailure), otherwise FAILED. A group whose dependency failed (and didn't
ignore it) is classified blocked — it will never get a CurrentBuildSummary, so it
must still count toward FAILED rather than stall the batch at IN_PROGRESS forever.
StopBuildBatch is the one status transition NOT derived: it force-stops every
in-progress child and sets STOPPED directly, matching the issue's own framing exactly.
Disclosed (see structural_gaps: above): CombineArtifacts/BatchReportMode are
passthrough config only, no real effect.
Snapshot decision. All Build/BuildBatch changes are additive fields — no
existing field's JSON key or type changed. backendSnapshot gained one new additive
map field, buildBatchNumbers (tracks each project's next BuildBatchNumber, matching
real AWS's per-project monotonic numbering that survives individual batch deletion —
types/types.go:313's doc comment). codebuildSnapshotVersion (currently 2) was left
unchanged: go test ./pkgs/persistence/... -run TestSnapshotVersionGuard failed exactly
as its own message predicts for a purely-additive change ("every old field is still
present unchanged, so the diff is additive only and needs no bump") and was accepted via
-update. Inventory diff (pkgs/persistence/testdata/snapshot_inventory.json): one new
Build field (BuildBatchArn), four new types (BuildGroup, BuildSummary,
BuildBatchPhase, ResolvedArtifact), BuildBatch grew from 7 fields to the full
shape above, and backendSnapshot gained buildBatchNumbers — no existing entry in
that file changed or was removed.
Tests. build_batches_graph_test.go (new, through the real typed
aws-sdk-go-v2/service/codebuild client, matching this package's
newTestCodeBuildClient convention): TestStartBuildBatch_BuildGraph drives a 3-node
build-graph (c depends on a, b) to SUCCEEDED, asserting BuildGroups'
DependsOn/CurrentBuildSummary shape, a real child's BuildBatchArn +
inherited environment via BatchGetBuilds, and ListBuildsForProject including all 3
children. TestStartBuildBatch_EnvironmentOverride proves an environment override
reaches the batch's child but never mutates the project (BatchGetProjects after).
TestStartBuildBatch_MaximumBuildsAllowedExceeded and
TestStartBuildBatch_NoBatchSection (table: missing section, empty buildspec) assert
the real InvalidInputException. TestStopBuildBatch_StopsInProgressChildren asserts
every started child reads STOPPED after StopBuildBatch. runJanitorUntilBatchTerminal
drives the Janitor's SweepOnce (no time.Sleep, no real timer) inside
require.Eventually until the batch leaves IN_PROGRESS. All five fail against the
pre-fix code: the old BuildBatch had no BuildGroups/Environment fields at all (the
SDK response simply decodes them as empty/nil, so every require.Len/require.NotNil
above them fails), StartBuildBatch never validated the buildspec (so the two
error-path tests get a 200 where they expect 400), and StopBuildBatch never touched
children (though TestStopBuildBatch_StopsInProgressChildren's own pre-stop
require.Len(startOut.BuildBatch.BuildGroups, 3) already fails there independently).
Existing tests updated for the new "a project's buildspec needs a batch: section to
start a batch" precondition: build_batches_test.go/pagination_test.go (added
createBatchProject/testSingleNodeBatchSpec, a single-node build-list, to
handler_test.go), janitor_test.go/persistence_test.go (added the same buildspec to
their direct-backend project setup and threaded StartBuildBatch's new
StartBuildBatchConfig parameter). persistence_test.go's full-state round-trip test
now expects 2 builds for proj1 (the standalone StartBuild plus the batch's 1
build-list child) rather than 1 — a real behavior change (batch children are now real,
listable Build records), not a loosened assertion.
Gates: go build ./... (whole module), go vet ./..., go test -count=1 ./services/codebuild/... ./pkgs/persistence/..., go test -race -count=1 ./services/codebuild/..., golangci-lint run ./services/codebuild/... — 0 issues
before code existed and 0 issues after (fieldalignment -fix applied to the four new
structs; unused/goconst/golines/nolintlint/govet-shadow findings during
development were all fixed, not suppressed).
Drove the 38 previously-typed-client-uncovered ops through a real aws-sdk-go-v2
client in realclient_fleet_build_and_report_management_test.go: fleet lifecycle
(CreateFleet/BatchGetFleets/ListFleets/UpdateFleet/DeleteFleet), report group +
report lifecycle (DeleteReport/DescribeCodeCoverages/DescribeTestCases/
GetReportGroupTrend/ListReportGroups/ListReports/ListReportsForReportGroup/
ListSharedReportGroups/UpdateReportGroup, reports seeded via the existing
AddReportInternal test helper since CodeBuild has no direct report-create
API), build batch + build lifecycle (DeleteBuildBatch/ListBuildBatches/
ListBuildBatchesForProject/RetryBuildBatch/ListBuilds/StopBuild/
BatchDeleteBuilds), sandbox + command execution lifecycle
(BatchGetCommandExecutions/ListCommandExecutionsForSandbox/ListSandboxes/
ListSandboxesForProject/StartSandboxConnection/StopSandbox),
ImportSourceCredentials/ListSourceCredentials, CreateWebhook/UpdateWebhook/
DeleteWebhook, PutResourcePolicy/GetResourcePolicy/DeleteResourcePolicy,
ListCuratedEnvironmentImages, UpdateProjectVisibility/ListSharedProjects, and
InvalidateProjectCache.
One real wire bug found and fixed: StartSandboxConnection's response was
a fabricated {"endpoint": "wss://..."} object -- a field the real
StartSandboxConnectionOutput does not have at all. The real member is
ssmSession, an SSMSession{sessionId,streamUrl,tokenValue} object
(codebuild@v1.72.4 api_op_StartSandboxConnection.go:38-41,
types/types.go:2805, confirmed against the deserializer's own
case "ssmSession" at deserializers.go:16919). A real client's
out.SsmSession was always nil regardless of what gopherstack sent --
this op's wire grade was already disclosed as partial in PARITY.md's ops
table for a different, legitimate reason (no real Session Manager streaming
is simulated), but the shape itself being wrong was undiscovered until
decoded through a real typed client. Fixed by adding an SSMSession type
(models.go) and emitting it with synthesized placeholder values; the
"no real interactive terminal" limitation remains and is unchanged (see
ops-table note). This is not a persisted (backendSnapshot) field --
StartSandboxConnection's output is ephemeral, not stored on Sandbox --
so no snapshot_inventory.json change was needed for this fix, confirmed by
TestSnapshotVersionGuard staying green throughout.
No other bugs found across the remaining 37 ops; all passed on the first correctly-shaped request, consistent with this service's dense prior audit history (2026-08-13/23/29/31, 2026-09-04 passes already listed above).
Gates: go build ./... (whole module, clean). go vet ./services/codebuild/...
clean. go test -race -count=1 ./services/codebuild/... and
./pkgs/persistence/... clean (no snapshot diff). golangci-lint run --new-from-rev=HEAD ./services/codebuild/... 0 issues. go run ./cmd/paritylint stayed at 0 FAIL (missing-items-still-open) throughout. No
version bump; no items_still_open changes needed (the one bug found was
fixed outright, not disclosed as a new gap).
cmd/reqfielddiff flagged 7 tier-1 fields. Three were false positives,
already declared and applied: CreateProject.SourceVersion/
UpdateProject.SourceVersion (handler_projects.go, projects.go:215-216) and
CreateProject.TimeoutInMinutes (handler_projects.go:31, projects.go:246-247).
The other four were real:
ImportSourceCredentials.ShouldOverwrite: decoded nowhere before this pass, so any value (including the real default oftrue) had no effect -- a re-import for a serverType that already had stored credentials always silently overwrote them. Now decoded and enforced:ShouldOverwrite=falseagainst an existing serverType credential rejects withResourceAlreadyExistsException(ErrAlreadyExists); unset ortruestill overwrites, matching the real documented default.DescribeTestCases.MaxResults/NextToken,ListSharedProjects. MaxResults,ListSharedReportGroups.MaxResults: none of the three input structs decoded pagination at all. All three operate over lists this backend can never grow past zero/one page today (no test-case-content ingestion pipeline for DescribeTestCases; no cross-account resource- sharing model at all for the two ListShared* ops -- both pre-existing, disclosed gaps, seeitems_still_open/theListSharedProjects/ListSharedReportGroupsop rows above), so truncation itself can't be demonstrated with real data. Wired anyway (via the existingpaginateIDs/newpaginateTestCaseshelpers in pagination.go) because the fields are real, and correctly proven via the other observable effect available: a malformedNextTokenis now rejected (page.ValidateToken) instead of being silently ignored, which was the actual pre-fix behavior for all three.
New tests: TestImportSourceCredentials_ShouldOverwrite and
TestPaginationParams_HonourMalformedNextToken (table-driven, covering all
three pagination ops) in
realclient_fleet_build_and_report_management_test.go, driven through the
real aws-sdk-go-v2/service/codebuild client.
Gates: go build ./... (whole module) clean; go vet ./services/codebuild/... clean; go test -race -count=1 -p 2 ./services/codebuild/... ok; golangci-lint run --concurrency 2 --new-from-rev=HEAD ./services/codebuild/... 0 issues; go run ./cmd/paritylint 0 FAIL. No persisted fields changed, no inventory rows,
no version bump.
Audited clean: sortOrder/sortBy, build-batch and report status filters. No change.
codebuild is region-isolated: Projects, builds, batches, report groups and source credentials live per region; each sibling runs its own build janitor. CodePipeline Build actions start builds in the pipeline region. Tagging bridge not wired (none before). Per-region sibling handlers via pkgs/regionpeers; snapshots gain an additive regions key only when a sibling exists (no version bump; older snapshots restore). NewHandler alone stays single-region. Proof: TestHandler_MultiRegionIsolation, TestHandler_MultiRegionPersistence, TestRegionIsolation/codebuild. Limitation: the dashboard shows the home region only.
FIXED: DescribeCodeCoverages now takes maxResults/nextToken and rejects a malformed token with InvalidInputException (TestPaginationParams_HonourMalformedNextToken). RECORDED: DescribeCodeCoverages min/max coverage and sort members and DescribeTestCases Filter are unsupported because both ops always return an empty list (no coverage/test-case ingestion), so a filter has nothing to act on.
StartBuild, RetryBuild, StartBuildBatch, RetryBuildBatch and StartSandbox honour IdempotencyToken: a repeat within five minutes returns the original resource, a repeat with different parameters is InvalidInputException (the real mismatch code is undocumented), and the memo is capped at 1024 entries. StartBuild and StartBuildBatch apply LogsConfigOverride; builds report the effective CloudWatch/S3 logs configuration in logs and batches in logConfig. UpdateProjectVisibility stores ResourceAccessRole. Proof: dropped_members_sdk_test.go. The memo is not persisted; Build.Logs gains additive omitempty cloudWatchLogs/s3Logs.
40 request members typed as SDK enums are now checked against the SDK Values() and an unknown value is InvalidInputException (Create/UpdateFleet compute/environment/overflow, CreateReportGroup type, Create/UpdateWebhook buildType, DescribeCodeCoverages sortBy/sortOrder, ImportSourceCredentials, RetryBuildBatch, StartBuild/StartBuildBatch overrides, UpdateProjectVisibility, shared-resource sortBy and every List* sortOrder). Proof: TestSDK_EnumInputValidation (rejects an invalid value, accepts every Values() entry). RECORDED: StartCommandExecution type is not validated because existing tests send COMMAND while types.CommandType only lists SHELL; DescribeCodeCoverages sortBy/sortOrder are validated but have no effect (the op always returns an empty list).
Build IDs are project:<uuid>. When the janitor completes a build it records the full phase timeline (QUEUED through FINALIZING, then COMPLETED). StopBuild on a finished build returns it unchanged instead of rewriting SUCCEEDED to STOPPED. Create/UpdateProject validate the name pattern, timeouts (5-2160, queued 5-480) and environment/source/artifacts enums; StartBuild bounds timeoutInMinutesOverride. NotFound/AlreadyExists carry AWS-style messages and no code prefix. Lenient on purpose: serviceRole is not ARN-checked (fixtures use placeholders). Tests: realism_validation_test.go.