Summary
In 01-features/07-centralize-and-govern-your-ai-infrastructure/03-registry/04-migrate-to-new-namespace, the config setting runtime.transform.duplicateNames (values "fail" / "suffix", documented in docs/configuration.md and docs/detaileddoc.md) has no effect when set to "suffix". Records that collide on name+recordVersion in the target namespace still hard-fail at load time exactly as if the setting were absent or set to "fail".
Repro
- Preview (
bedrock-agentcore) registry has ≥2 records with the same name and no/matching recordVersion (legal there — name was just a display label).
migration.config.json sets:
"runtime": { "transform": { "duplicateNames": "suffix" } }
- Run
init/target-config --create → check → extract → load --dry-run (or --live).
Expected (per docs): colliding records are migrated under distinct, deterministically-suffixed target names (suffix "derived from the source record's identity" per docs/configuration.md), nothing is dropped.
Actual: each colliding record after the first fails with:
Preview records '<acct>/<region>/<reg>/<idA>' and '<acct>/<region>/<reg>/<idB>' both migrate to the target registry name '<name>' (recordVersion '<version>'). That identity is already claimed in registry <target>; the new version requires it to be unique, so loading the second would overwrite the first. Rename one of them in the source registry, or give them distinct recordVersions, and re-extract.
identical whether duplicateNames is "fail" (default), "suffix", or absent from the config entirely.
Root cause
duplicateNames is never read by the code that actually claims target names. Searching the whole tool subtree:
grep -r duplicateNames lib/ cli/ dist/ glue/common/migration_common/ → zero matches in any implementation file.
- It only appears in
docs/configuration.md, docs/detaileddoc.md, and in comments/test scaffolding (glue/common/tests/test_jobs_end_to_end.py, glue/common/tests/test_report_html.py, tools/seed_preview_test_registry.py).
- The actual failure originates in
glue/common/migration_common/jobs/transform_load.py (claim_in_order) → glue/common/migration_common/registry_api.py (claim / _claim_target_record), which raises unconditionally on a name collision with no branch on the configured duplicateNames mode.
- Notably,
tools/seed_preview_test_registry.py (lines ~52-63, ~512-517) documents the intended behavior in comments — extract should detect the clash and resolve it once duplicateNames="suffix" — and states a duplicate-name fixture was deliberately removed from the seed matrix "because... extract detects the clash... and resolves it once runtime.transform.duplicateNames is set to suffix." That resolution path does not exist in the shipped code, so the fixture's removal currently masks a live gap in test coverage for this setting.
Default ("fail"/unset) behavior otherwise matches its documented contract (the run completes with PARTIAL_SUCCESS, colliding records are skipped and listed in the failure report) — this issue is scoped specifically to "suffix" mode being a no-op.
Impact
With the default runtime.load.failOnRecordError=false, a migration under an unattended/Glue-managed run can silently skip colliding records while reporting PARTIAL_SUCCESS — a user who set duplicateNames="suffix" believing it protects against this will not get an error signal distinct from the unset case, and could lose records without realizing it unless they inspect the failure report closely.
Suggested fix
Wire duplicateNames through to claim_in_order/claim in migration_common: when "suffix", assign each colliding record (after the first claimant) a deterministic target name suffix derived from the source record's identity, preserve the original name as displayName/previewName per the existing crosswalk semantics, and continue instead of raising. Happy to open a PR for this — will follow up.
Summary
In
01-features/07-centralize-and-govern-your-ai-infrastructure/03-registry/04-migrate-to-new-namespace, the config settingruntime.transform.duplicateNames(values"fail"/"suffix", documented indocs/configuration.mdanddocs/detaileddoc.md) has no effect when set to"suffix". Records that collide onname+recordVersionin the target namespace still hard-fail at load time exactly as if the setting were absent or set to"fail".Repro
bedrock-agentcore) registry has ≥2 records with the samenameand no/matchingrecordVersion(legal there —namewas just a display label).migration.config.jsonsets:init/target-config --create→check→extract→load --dry-run(or--live).Expected (per docs): colliding records are migrated under distinct, deterministically-suffixed target names (suffix "derived from the source record's identity" per
docs/configuration.md), nothing is dropped.Actual: each colliding record after the first fails with:
identical whether
duplicateNamesis"fail"(default),"suffix", or absent from the config entirely.Root cause
duplicateNamesis never read by the code that actually claims target names. Searching the whole tool subtree:grep -r duplicateNames lib/ cli/ dist/ glue/common/migration_common/→ zero matches in any implementation file.docs/configuration.md,docs/detaileddoc.md, and in comments/test scaffolding (glue/common/tests/test_jobs_end_to_end.py,glue/common/tests/test_report_html.py,tools/seed_preview_test_registry.py).glue/common/migration_common/jobs/transform_load.py(claim_in_order) →glue/common/migration_common/registry_api.py(claim/_claim_target_record), which raises unconditionally on a name collision with no branch on the configuredduplicateNamesmode.tools/seed_preview_test_registry.py(lines ~52-63, ~512-517) documents the intended behavior in comments — extract should detect the clash and resolve it onceduplicateNames="suffix"— and states a duplicate-name fixture was deliberately removed from the seed matrix "because... extract detects the clash... and resolves it onceruntime.transform.duplicateNamesis set tosuffix." That resolution path does not exist in the shipped code, so the fixture's removal currently masks a live gap in test coverage for this setting.Default (
"fail"/unset) behavior otherwise matches its documented contract (the run completes withPARTIAL_SUCCESS, colliding records are skipped and listed in the failure report) — this issue is scoped specifically to"suffix"mode being a no-op.Impact
With the default
runtime.load.failOnRecordError=false, a migration under an unattended/Glue-managed run can silently skip colliding records while reportingPARTIAL_SUCCESS— a user who setduplicateNames="suffix"believing it protects against this will not get an error signal distinct from the unset case, and could lose records without realizing it unless they inspect the failure report closely.Suggested fix
Wire
duplicateNamesthrough toclaim_in_order/claiminmigration_common: when"suffix", assign each colliding record (after the first claimant) a deterministic targetnamesuffix derived from the source record's identity, preserve the original name asdisplayName/previewNameper the existing crosswalk semantics, and continue instead of raising. Happy to open a PR for this — will follow up.