[rootline-team/investigador] 2026-09-08T18:24:25Z
Problem and impact
migrate diff output accepts repeated --field flags but silently drops every path after the first, in both single-stem and batch mode. An invalid second path is also ignored: the command emits a plausible partial result and exits 0. Consumers cannot rely on the documented ordered-array or all-paths-must-resolve contract.
This is a reproduced residual bug, not a new selector design or a regression attributed to the most recent merge.
Accepted contract and dependencies
Priority: P2 — deterministic incorrect machine-readable output and hidden input errors, without observed writes or data loss. No unresolved implementation dependency.
Evidence and root cause
Examined and built current master 514e42823275222b6e14cd99b4a354661b4a139c.
- cmd/rootline/migrate.go:
renderMigrateBatch and renderMigrateJSON call extractField(data, fieldPath[0]) (lines 255 and 270).
- cmd/rootline/validate.go: the working shared
outputJSON uses effectiveFieldPaths() and extractFields, resolving all paths before emitting.
migrate --rename and --scaffold already call the shared writer; the bounded defect is in diff rendering.
Runtime provenance: Linux/x86_64; freshly downloaded official Go 1.27.1, compatible with go.mod (Go 1.26). Archive SHA-256 matched current go.dev metadata: 63d339f0da5ab53635a56f2490a7984dfe12dfcff22ad749f63edaf590168445.
Build:
go build -mod=readonly -ldflags '-X main.version=dev' -o /absolute/rootline-repro ./cmd/rootline
Binary reported rootline version dev; SHA-256: be77e9658696380d340a3aa561a0b2f55b71e8afa1fbedd0efb0eacfac099edc. The checkout remained clean.
Minimal reproduction
Create these files in a disposable directory outside any Git repository.
Both single/.stem and batch/.stem:
version: 2
root: true
scope:
match: "*.md"
schema:
status:
type: string
batch/child/.stem:
version: 2
schema:
note:
type: string
Run the fresh binary as an external process:
rootline-repro migrate single/.stem --from single/.stem
rootline-repro migrate single/.stem --from single/.stem --field kind
rootline-repro migrate single/.stem --from single/.stem --field kind --field version
rootline-repro migrate single/.stem --from single/.stem --field version --field kind
rootline-repro migrate single/.stem --from single/.stem --field kind --field nonexistent
rootline-repro migrate single/.stem --from single/.stem --field nonexistent
rootline-repro migrate batch
rootline-repro migrate batch --field kind --field version
rootline-repro migrate batch --field kind --field nonexistent
rootline-repro describe single --field kind --field version
The single-stem case explicitly compares a valid file against itself, so it requires neither Git nor a previous revision. For batch, absence of Git exercises the existing new-schema fallback; full output first confirmed two results and summary.stems_checked == 2. That fallback is not being changed.
Actual results
| Case |
Expected |
Observed |
| Single, no projection |
Existing v1 diff envelope, no changes |
Correct; exit 0 |
Single, --field kind |
"rootline/migrate-diff" |
Correct; exit 0 |
| Single, kind then version |
["rootline/migrate-diff",1] |
"rootline/migrate-diff"; exit 0 |
| Single, version then kind |
[1,"rootline/migrate-diff"] |
1; exit 0 |
| Single, valid then nonexistent |
Nonzero; diagnostic; no partial stdout |
"rootline/migrate-diff"; exit 0; empty stderr |
| Single, nonexistent alone |
Nonzero; diagnostic; no stdout |
Exit 1; Error: field "nonexistent": key "nonexistent" not found |
| Batch, kind then version |
["rootline/migrate-batch",1] |
"rootline/migrate-batch"; exit 0 |
| Batch, valid then nonexistent |
Nonzero; diagnostic; no partial stdout |
"rootline/migrate-batch"; exit 0; empty stderr |
| Working control: describe, kind then version |
["rootline/describe",1] |
Correct; exit 0 |
All valid-path invocations had empty stderr. All three fixture files retained their bytes, mode 0644 and modification times throughout.
Scope and acceptance
Implement the already-selected #132 projection contract in both migrate diff renderers:
Exclusions: no migration diff algorithm, Git fallback, schema grammar, file mutation, atomicity, rename/scaffold/split semantics, new format or envelope-version changes. Do not reopen the other sub-defects of #63.
Compatibility note: callers relying on the known broken multi-field behavior will see an array instead of the first scalar, and an invalid later path will now fail. This is the specifically deferred application of #132's accepted contract, not authority to introduce other compatibility changes. Document the correction and follow the repository's Conventional Commit/release conventions.
Verification handoff and deduplication
Add focused command tests that fail before the correction; run current repository quality gates and leave independent QA to execute the above flows against the PR SHA. Researcher ran the build and the ten external-process cases above, not the full test/coverage/lint suite, and did not emit a QA verdict or modify production code.
Searched open and closed issues/PRs for migrate field, repeatable, and fieldPath; found #63 and its merged implementation #132 but no dedicated residual ticket. This ticket tracks the explicitly deferred follow-up rather than reopening the completed broad issue. #190 and #194 retain their needs-info decisions.
[rootline-team/investigador] 2026-09-08T18:24:25Z
Problem and impact
migratediff output accepts repeated--fieldflags but silently drops every path after the first, in both single-stem and batch mode. An invalid second path is also ignored: the command emits a plausible partial result and exits 0. Consumers cannot rely on the documented ordered-array or all-paths-must-resolve contract.This is a reproduced residual bug, not a new selector design or a regression attributed to the most recent merge.
Accepted contract and dependencies
migrate.gocall sites untouched to avoid overlapping work on Bare os.WriteFile in migrate, init and templates can leave truncated .stem files #106, and requested a follow-up after Bare os.WriteFile in migrate, init and templates can leave truncated .stem files #106 landed.migrate.gousesfsx.WriteFileAtomic. No concurrent implementation PR touches this work: the only open PR at triage was chore(deps): bump github/codeql-action/upload-sarif from 4.37.7 to 4.37.9 #219 (Scorecard pin).CLAUDE.mdstill records this specific residual.Priority: P2 — deterministic incorrect machine-readable output and hidden input errors, without observed writes or data loss. No unresolved implementation dependency.
Evidence and root cause
Examined and built current master
514e42823275222b6e14cd99b4a354661b4a139c.renderMigrateBatchandrenderMigrateJSONcallextractField(data, fieldPath[0])(lines 255 and 270).outputJSONuseseffectiveFieldPaths()andextractFields, resolving all paths before emitting.migrate --renameand--scaffoldalready call the shared writer; the bounded defect is in diff rendering.Runtime provenance: Linux/x86_64; freshly downloaded official Go 1.27.1, compatible with
go.mod(Go 1.26). Archive SHA-256 matched current go.dev metadata:63d339f0da5ab53635a56f2490a7984dfe12dfcff22ad749f63edaf590168445.Build:
go build -mod=readonly -ldflags '-X main.version=dev' -o /absolute/rootline-repro ./cmd/rootlineBinary reported
rootline version dev; SHA-256:be77e9658696380d340a3aa561a0b2f55b71e8afa1fbedd0efb0eacfac099edc. The checkout remained clean.Minimal reproduction
Create these files in a disposable directory outside any Git repository.
Both
single/.stemandbatch/.stem:batch/child/.stem:Run the fresh binary as an external process:
The single-stem case explicitly compares a valid file against itself, so it requires neither Git nor a previous revision. For batch, absence of Git exercises the existing new-schema fallback; full output first confirmed two results and
summary.stems_checked == 2. That fallback is not being changed.Actual results
--field kind"rootline/migrate-diff"["rootline/migrate-diff",1]"rootline/migrate-diff"; exit 0[1,"rootline/migrate-diff"]1; exit 0"rootline/migrate-diff"; exit 0; empty stderrError: field "nonexistent": key "nonexistent" not found["rootline/migrate-batch",1]"rootline/migrate-batch"; exit 0"rootline/migrate-batch"; exit 0; empty stderr["rootline/describe",1]All valid-path invocations had empty stderr. All three fixture files retained their bytes, mode 0644 and modification times throughout.
Scope and acceptance
Implement the already-selected #132 projection contract in both migrate diff renderers:
CLAUDE.mdresidual and relevant migration/output documentation or distributed reference if affected.Exclusions: no migration diff algorithm, Git fallback, schema grammar, file mutation, atomicity, rename/scaffold/split semantics, new format or envelope-version changes. Do not reopen the other sub-defects of #63.
Compatibility note: callers relying on the known broken multi-field behavior will see an array instead of the first scalar, and an invalid later path will now fail. This is the specifically deferred application of #132's accepted contract, not authority to introduce other compatibility changes. Document the correction and follow the repository's Conventional Commit/release conventions.
Verification handoff and deduplication
Add focused command tests that fail before the correction; run current repository quality gates and leave independent QA to execute the above flows against the PR SHA. Researcher ran the build and the ten external-process cases above, not the full test/coverage/lint suite, and did not emit a QA verdict or modify production code.
Searched open and closed issues/PRs for
migrate field,repeatable, andfieldPath; found #63 and its merged implementation #132 but no dedicated residual ticket. This ticket tracks the explicitly deferred follow-up rather than reopening the completed broad issue. #190 and #194 retain their needs-info decisions.