Severity: high. Independently named the single highest-value improvement by two user tests (a platform engineer running a 100-repo rollout, and a reorg manager). Pre-existing.
sync --format json / --summary-out is what docs/FLEET.md loops over. For a displacing change it carries no before-state at all.
Repro
The README's own advice for a displacing baseline is "run set_owners(*, …) on its own first". Run it against a repo with hand-curated ownership:
cot sync --repo danger --op 'set_owners(*, [@acme/everyone])'
applied: 1 op(s) applied, 0 skipped; 1 line change(s), 5 path(s) change owners
ops[0] applied (proven: tree) # exit 0
@acme/appsec and @acme/security-leads just lost the scanners directory. The change record:
{ "action": "insert", "line": 18, "pattern": "*",
"new_owners": ["@acme/everyone"],
"reason": "inserted after the last rule whose match set intersects the scope ... (R-3)" }
No old_owners (the record is line-level; an insert has no previous line), warnings: null, and the PR body a reviewer sees says only:
- status: `applied`
- paths whose owners change: 5
Five paths changed. Not "three teams stop owning things". Nothing in the sync path distinguishes co-owning 5 files from displacing 5 files' owners.
The information already exists
plan --out on the identical op emits full ownership_rows with owners_before: ["@acme/appsec","@acme/security-leads"] → owners_after: ["@acme/everyone"]. But plan is the deliberate two-step path and FLEET.md is explicit that a rollout loops over sync, so at fleet scale the artifact that would catch this is the one you do not have.
Suggested fix
Put owners_removed (or the whole ownership_rows) into the sync record, and give --summary-out an Owners losing access section whenever any path's owner set shrinks. The tool already computes it; this is surfacing, not new analysis.
Related: the R-8 refusal hands the operator a copy-pasteable instruction into exactly this unguarded operation — tracked separately in #35.
Severity: high. Independently named the single highest-value improvement by two user tests (a platform engineer running a 100-repo rollout, and a reorg manager). Pre-existing.
sync --format json/--summary-outis whatdocs/FLEET.mdloops over. For a displacing change it carries no before-state at all.Repro
The README's own advice for a displacing baseline is "run
set_owners(*, …)on its own first". Run it against a repo with hand-curated ownership:@acme/appsecand@acme/security-leadsjust lost the scanners directory. The change record:{ "action": "insert", "line": 18, "pattern": "*", "new_owners": ["@acme/everyone"], "reason": "inserted after the last rule whose match set intersects the scope ... (R-3)" }No
old_owners(the record is line-level; aninserthas no previous line),warnings: null, and the PR body a reviewer sees says only:Five paths changed. Not "three teams stop owning things". Nothing in the sync path distinguishes co-owning 5 files from displacing 5 files' owners.
The information already exists
plan --outon the identical op emits fullownership_rowswithowners_before: ["@acme/appsec","@acme/security-leads"] → owners_after: ["@acme/everyone"]. Butplanis the deliberate two-step path and FLEET.md is explicit that a rollout loops oversync, so at fleet scale the artifact that would catch this is the one you do not have.Suggested fix
Put
owners_removed(or the wholeownership_rows) into thesyncrecord, and give--summary-outan Owners losing access section whenever any path's owner set shrinks. The tool already computes it; this is surfacing, not new analysis.Related: the R-8 refusal hands the operator a copy-pasteable instruction into exactly this unguarded operation — tracked separately in #35.