Unchanged default-language save spells out defaults and rewrites the connected translation ("2 changes") #219
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#219
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Saving an element in the default language without changing anything rewrites its connected German translation, so the pending-changes chip shows "2 changes" for one no-op save. A save that changes nothing must leave both files alone.
Current behavior
The form describe seeds a param the content file lacks with its sidecar default (
Schema.describeininternal/element/form.go, viaParam.Empty). The form snapshot sends every field back on save (ElementForm,form.snapshot()), andelemops.MergeParamswrites every managed value it receives. So an element whose file omits an Appearance param with a default — e.g. the hero'salign(default: left,tab: appearance) — gainsalign="left"on an "unchanged" save: change number one. Because the save is in the default language,Handler.syncTranslations(internal/server/translation.go) runsl10n.SyncAppearancefor every connected translation; the German element lacksalign, the source now has it, so the German file is rewritten withalign="left": change number two.Confirmed reproduction (Go,
copyFixture(t, multilingualSiteDir)with languages en/de):POST /api/page/save {page:"services/webdesign/_index.md", lang:"en", path:[0], name:"hero", values:{title:"Web design", subtitle:"Sites that fit.", align:"left"}}— the values the form holds for that element. Before: neither_index.mdnor_index.de.mdhasalign. After: both files carryalign="left"; the German file was rewritten. Sending the same values withoutalignleaves both files byte-identical.Content hits this whenever a file omits an Appearance param that the sidecar defaults: hand-authored or migrated pages, translations created from such sources, and any element inserted before its sidecar gained the param. Elements inserted by the editor write their defaults at insert (
elemops.NewShortcode), so fresh editor-made content does not show it until a sidecar changes.Desired behavior
Decisions
SyncAppearanceorRepairactually changed it (already true today); the fix is that an unchanged save no longer produces a change to propagate.Key interfaces
elemops.MergeParams(existing, schema, clean)ininternal/elemops/save.goandParam.ShortcodeValue/Param.Initial/Param.Defaultininternal/element— where "write this param or not" is decided. Items rows (buildItemShortcodes) take the same decision for their fields.Schema.describeininternal/element/form.go— how a missing param's form value is seeded; the fix must not change what the form shows.Handler.syncTranslationsandl10n.SyncAppearance(internal/server/translation.go,internal/l10n/track.go) — unchanged in principle; add a regression test that an unchanged default-language save leaves the connected translation byte-identical, next toTestSourceOpsPropagateIntoConnectedTranslationsininternal/server/translation_ops_test.go.l10n.Hash/canonicalParam(internal/l10n/hash.go) — must treat an absent param and an explicit default as the same value, or the "source changed since this translation was saved" note would flip when a default is spelled out or dropped.Acceptance criteria
enwith the values the form holds (includingalign: "left") leaves_index.mdand_index.de.mdbyte-identical.align: "center"inenwrites it to both the source and the connecteddefile; a free translation is untouched.titleinenleaves the connecteddefile byte-identical.align="left"explicitly keeps it after an unchanged save (no churn in the other direction); an explicit non-default set back to the default stays explicit.scaffold/site/AGENTS.mddescribes which params a save writes (content canonicalization / element params) truthfully after the change;edit-contentandmigrate-siteskills revisited for any statement about defaults in files.How to verify
go test ./...with the new tests ininternal/serverandinternal/elemops;cd web && npm test.harness checkontestdata/multilingual-siteand the scaffold site still passes.Assumptions to check
Param.Defaultis normalized to the same Go type the form submits and the content parser yields (e.g. numbers), so "equals the default" compares correctly for every field type that can carry a default (select, bool, number, text, datetime, link with companions).internal/l10n/hash.go, the pending-list element diff (diffElementsininternal/server/pending.go), or theme templates reading a param without a fallback (the theme must already cope, since hand-authored content omits params)./api/page/settings/save) has the same default-spelling behavior for front matter; note it if so, but see Out of scope.Out of scope
elemops.NewShortcodewrites at insert.l10nSourcestamping of translations.Blocked by
None - can start immediately