chore: sync upstream (27 Commits) #7

Merged
alf merged 27 commits from chore/sync-upstream-20261023 into main 2026-10-23 11:23:56 +00:00
Owner

Upstream-Sync von upstream/main (27 Commits). Fast-Forward.

  • 3a4770ef Merge pull request #3286 from matstrange/fix/protection-inspect-tracking
  • debb125d #1327: Preserve file identity after rejected document replacement
  • 5220199b #1327: Keep snapshots with their document through Compare
  • 8280903f #3277: Test Inspector refusal without protection
  • 706d6cc7 A comparison clears the version snapshots of the document it replaces
  • 3323ffca Docs: the inspector's tracking refusal and the version snapshot's scope
  • 23c8dfa9 A version snapshot belongs to the document it was taken from
  • 080d2cae The inspector's tracking refusal doesn't cite an unrelated issue
  • f87ab00a The Document Inspector refuses its removals while the document tracks changes
  • c799fdbc Merge pull request #3285 from matstrange/fix/protection-file-commands
  • af2803a5 #3279: Keep protected session author fixed in CLI and Options
  • ff36e73e Docs: the session author, not the user name
  • ec1b5393 The inspector's removals are refused in every protected mode
  • 5151809e Merge origin/main into fix/protection-file-commands
  • 9130181a Release 0.10.0
Upstream-Sync von `upstream/main` (27 Commits). Fast-Forward. - 3a4770ef Merge pull request #3286 from matstrange/fix/protection-inspect-tracking - debb125d #1327: Preserve file identity after rejected document replacement - 5220199b #1327: Keep snapshots with their document through Compare - 8280903f #3277: Test Inspector refusal without protection - 706d6cc7 A comparison clears the version snapshots of the document it replaces - 3323ffca Docs: the inspector's tracking refusal and the version snapshot's scope - 23c8dfa9 A version snapshot belongs to the document it was taken from - 080d2cae The inspector's tracking refusal doesn't cite an unrelated issue - f87ab00a The Document Inspector refuses its removals while the document tracks changes - c799fdbc Merge pull request #3285 from matstrange/fix/protection-file-commands - af2803a5 #3279: Keep protected session author fixed in CLI and Options - ff36e73e Docs: the session author, not the user name - ec1b5393 The inspector's removals are refused in every protected mode - 5151809e Merge origin/main into fix/protection-file-commands - 9130181a Release 0.10.0
alf added 27 commits 2026-10-23 11:23:56 +00:00
`always_allowed` waved through every `file.*` id, so on a protected document
the file commands that do change it went unchecked: the privacy options and
the properties wrote document settings, and the inspector's removals deleted
content. The privacy options' own comment already said they were "refused on
a protected document"; that wasn't true.

The allow-list now names the file commands that don't change the open
document, and the ones that do are checked like any other command. Reading
the document stays allowed: the inspector without removals, the privacy
settings, the info, and saving a version snapshot.

Restoring a stored version replaces the whole document, and a snapshot's own
protection is stale — restoring one lifted the protection and dropped its
password. The live document's protection, password and formatting limit now
survive a restore.

Co-Authored-By: opencode <noreply@opencode.ai>
`file.setAuthor` sets the name a protected document's named exception
ranges are checked against, and the author new revisions and comments are
recorded under. `always_allowed` waved through every `file.*` id, so on a
protected document the name could be changed: an agent could take the
identity of a named editor and unlock their exception range, and forge the
authorship of the revisions it leaves behind.

A protected document refuses the change now, with the rule Word uses for a
content control's properties: stop protection first. The check is in the
command itself — `file.setAuthor` is a pure command, so `check_before`
never sees it — and in `check_before` for the day it isn't.

Word lets the user name change under protection: it is a preference there.
This project doesn't, because that identity gates what may be edited.

Co-Authored-By: opencode <noreply@opencode.ai>
The guard in `restrict` refused a protected document only when its
protection had a password, so a password-less one could have its
protection changed — and `file.protect {"mode": "none"}` stopped it
outright. An agent could strip a user's protection silently and then edit
the document freely.

Two rules now, as Word has them (Stop Protection first):

- The protection of a protected document doesn't change, whatever the
  mode. The formatting-only protection doesn't count: it stores the mode
  "none", and it limits the styles, not the editing.
- Stopping the protection is explicit. The "none" mode is the shorthand
  for it, but on a protected document only `{"stop": true}` stops it — the
  way the pane's Stop Protection button does it — so a command that
  merely names a mode can't strip it.

The password still guards the stop, and the formatting-only path (the
mode "none" with the formatting restriction) still works.

`cmd::tools::tests::protection_blocks_edits` stops the protection with
`{"mode": "none"}`; it has to stop it explicitly now.

Co-Authored-By: opencode <noreply@opencode.ai>
`outside_unchanged` is the check that rolls a command back when it
changed anything outside the editable regions. It compared the text, the
formatting, the page setup, the document defaults and the styles — but
not the document's settings, its metadata, its custom properties, the
resolved flag of its comments, its list definitions or its bibliography
sources. A command that changed the metadata of a protected document was
accepted and never rolled back: `file.properties {"title": "Forged
title"}` on a protected document with an exception changed the title and
stayed.

The settings, the core properties, the custom properties, the comments'
resolved flags, the list definitions and the sources are compared now, so
a command that changes them is rolled back like any other change outside
the editable regions.

The revisions and the media store stay out of it on purpose: an edit
inside an exception legitimately leaves a revision, and a picture
inserted there adds to the media store. The list definitions and the
sources are compared like the styles: the ones the document had must be
unchanged, but a new one may be added — a list or a citation inserted
inside an editable region adds its definition, like applying a style adds
one.

Saving is unaffected: it writes the metadata, but `file.save` is a pure
command and isn't post-checked, and the protection itself is changed by
`review.restrict`, which is always allowed.

Co-Authored-By: opencode <noreply@opencode.ai>
The test that stopped the protection used the "none" shorthand, which a
protected document now refuses; it stops it the explicit way instead, and
pins that the shorthand doesn't stop it. `file.protect` says the same: it
starts enforcement, and "none" doesn't stop a protected document.

Co-Authored-By: opencode <noreply@opencode.ai>
Co-Authored-By: opencode <noreply@opencode.ai>
The guard that stops the protection of a protected document from changing
asked whether the editing was restricted, so a formatting-only protection —
the mode "none", which limits the styles rather than the editing — was
exempted. A formatting-only protection can carry a password, and that password
guarded it: it was exempt from the guard, so `{"mode": "readOnly", "password":
"other"}` replaced the protection and the password with ones of the caller's
choosing, and the protection could then be stopped with them.

A password guards the protection whatever its mode, as it did before.

Co-Authored-By: opencode <noreply@opencode.ai>
`--author` dispatched `file.setAuthor`, which a protected document refuses
now that the name is checked as the identity its exceptions are matched
against — so a script that named itself on a protected file failed before it
ran a single command.

The flag names the session, not the open document, so it sets the name
directly, with the same normalisation and blank-name check the command uses.
`file.setAuthor` keeps its protection check for the callers that mean to
change the document's identity.

Co-Authored-By: opencode <noreply@opencode.ai>
Co-Authored-By: opencode <noreply@opencode.ai>
Two gaps the review found, both of them a change to a protected document that
went through without the protection seeing it.

The properties (`file.properties`) and the privacy options
(`file.privacyOptions`) are document-wide state, and they aren't tracked: under
tracked changes they changed the document with no revision to show for it, and
under the other modes they were only rolled back by the post-check, after the
command had run. They are refused now, before the command runs, as the content
control's properties are.

A version restore (`file.versions`) replaces the whole document, including the
content the protection kept read-only, and it did that without the password:
`file.versions` is a pure command, so the protection never saw it. Keeping the
live protection and its password stopped them being dropped, but the content
underneath was still replaced. A restore needs the protection stopped first,
and the password with it when the document has one.

The live protection still wins over the snapshot's stale one, so a restore
can't lock the user out of their own document either.

Co-Authored-By: opencode <noreply@opencode.ai>
README: the app icon above the title
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the conflicts with the changes that landed since the branch was cut:

- crates/engine/src/cmd/protect.rs: the forms-mode refusals stay behind
  upstream's `greyed_out()` (so the ribbon still greys the Developer tab's
  control buttons out), and the refusal of the document's properties and its
  privacy options is added on top of it.
- ROADMAP.md, docs/file-format-parity.md, docs/gaps.md: both sides' entries
  are kept; the status lines carry the merge's date and this change at the
  front.

Co-Authored-By: opencode <noreply@opencode.ai>
`file.inspect` with removals takes content out of the document — the
comments, the revisions, the properties, the hidden text, the headers.
Under readOnly and comments it was refused, or rolled back by the
post-check where the document had an exception; under trackedChanges
nothing stopped it, so a document that tracks changes could have its
comments and revisions taken out with no revision to show for it.

The removals are refused now, in every mode a protected document can be
in, next to the properties and the privacy options. An inspection without
removals only reads the document, and still works.

The protection test covers the modes now, not only readOnly.

Co-Authored-By: opencode <noreply@opencode.ai>
"the user name can't change while the document is protected" was the
wrong term twice over: it is the session author — the name new tracked
changes and comments are recorded under, and the one a named exception
is matched against — and it is `file.setAuthor` that refuses to change
it. The CLI's `--author` names the session directly, so it works on a
protected document.

The parity row, the two status lines and the two progress rows now say
that.

Co-Authored-By: opencode <noreply@opencode.ai>
Restrict Editing: the file commands, the user name and the post-check
The inspector's removals delete straight from the document: the hidden
text, the comments, the revisions and the headers go without ever reaching
the engine's tracked-delete path. On a document that tracks changes — and
`trackedChanges` protection turns tracking on — the content was gone with
no revision to show for it, and the revision trail was broken behind the
user's back.

The removals are not tracked, so they cannot be: rather than untrack them
silently, the inspector refuses the whole removal form while the document
tracks changes, and says why ("the document tracks changes: stop tracking
before removing content"). The check comes before anything is removed, so
the document is left untouched, and reading the document — the inspector
without removals — keeps working. A document that does not track changes
is inspected as before.

Co-Authored-By: opencode <noreply@opencode.ai>
#1344 is about the inspector missing hidden text, not about its removals
going untracked, so the reference only sent a reader to the wrong place.

Co-Authored-By: opencode <noreply@opencode.ai>
`Session::set_document` resets everything about the open document — the
document id, the document, the selection, the undo history, the password,
the file name — but not the version snapshots. A snapshot taken of one
document therefore survived `file.new`, `file.open`, `file.newFromTemplate`
and `file.recover`, and `file.versions {"restore": index}` then put the
previous document's content into the open one.

The snapshots go with the document they were taken from: `set_document`
clears them, so a restore can only ever bring back a version of the
document that is open. Every caller replaces the document with another
one (a new or opened file, a recovered copy, a merge result, a sheet of
labels), so none of them loses anything it needs.

Co-Authored-By: opencode <noreply@opencode.ai>
Co-Authored-By: opencode <noreply@opencode.ai>
`review.compare` and `review.combine` open the original with `s.doc = doc`,
which bypasses `set_document` — so the version snapshots of the document the
comparison replaces survived it, and a restore could put the previous document
back over the comparison result.

The snapshots go with the document they were taken from, as everywhere else.
The document itself is still assigned directly: `set_document` resets the undo
history, and the comparison's own undo step would go with it.

Co-Authored-By: opencode <noreply@opencode.ai>
Merge pull request #3286 from matstrange/fix/protection-inspect-tracking
Some checks failed
nix.yml / Merge pull request #3286 from matstrange/fix/protection-inspect-tracking (pull_request) Failing after 0s
sonarcloud.yml / Merge pull request #3286 from matstrange/fix/protection-inspect-tracking (pull_request) Failing after 0s
nix.yml / Merge pull request #3286 from matstrange/fix/protection-inspect-tracking (push) Failing after 0s
sonarcloud.yml / Merge pull request #3286 from matstrange/fix/protection-inspect-tracking (push) Failing after 0s
Printing / Print (macos-latest) (push) Has been cancelled
Printing / Print (ubuntu-24.04) (push) Has been cancelled
Printing / Print (windows-latest) (push) Has been cancelled
CI / cargo xtask ci (push) Has been cancelled
CI / cargo xtask ci (pull_request) Has been cancelled
Printing / Print (macos-latest) (pull_request) Has been cancelled
Printing / Print (ubuntu-24.04) (pull_request) Has been cancelled
Printing / Print (windows-latest) (pull_request) Has been cancelled
3a4770ef8c
The inspector's removals are tracked, and a version snapshot belongs to its document
alf merged commit 3a4770ef8c into main 2026-10-23 11:23:56 +00:00
alf deleted branch chore/sync-upstream-20261023 2026-10-23 11:23:57 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
iris/wordcraft!7
No description provided.