Ensure web edits preserve comments as much as possible #34
Labels
No labels
Bug
BuildIssue
Claude-fixed
Enhancement
In Progress
Low priority
On Hold
Rejected
Security
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
keyop-go/nfq_forwarder#34
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?
Ideally any edits via the Web UI should attempt to preserve surrounding comments as well as any preamble comments.
changed the description
Plan
Found (by reading the actual code, not assumed) that comments are destroyed in two
independent places, not one:
resolveSubmittedYAML'syaml.Marshal(req.Config)(structured-form save path) - a freshconfig.Configurationstruct has no memory of the original file's comments at all.config_edit.js'sswitchToRaw()unconditionally overwrites the raw textarea withserver-marshaled (comment-free) YAML on every form→raw toggle - comments are lost just by
glancing at the structured form and switching back, even without ever saving.
Root cause of both:
config.Configurationcarries no comment/structure metadata at all - plainGo structs, only
json/yamltags.gopkg.in/yaml.v3(already a dependency) supports exactlythis via its
yaml.Nodetree (HeadComment/LineComment/FootCommentper node) - confirmedempirically against the real library: a preamble comment lands in
HeadCommenton the keynode, an inline comment lands in
LineCommenton the value node, and a comment before asequence item lands in
HeadCommenton the item node itself - which is your suggestedpreamble-block convention; it's already how yaml.v3 represents it, nothing extra needed to honor
that.
Design
New
internal/config/comments.go:MergeIntoYAML(baseYAML []byte, newConfig *Configuration)marshals
newConfigthe ordinary way into a comment-free "shape" tree, then splices the basedocument's comments onto matching nodes of that shape tree (the shape's own values are always
kept - they're already correct, only comments move):
defaults/web_ui): copy head/line/foot comments directlyonto the matching key/value nodes.
connections,defaults.by_network,forwarded_ports): match items by naturalidentity, not position, so a reorder doesn't strand a comment on the wrong entry -
connectionsbyname(falling back toqueue_number, mirroringConnection.Label()'s ownprecedence),
by_networkbycidr,forwarded_portsby the port value itself. An added itemgets no comments (correct - freshly marshaled); a removed item's comments simply go with it -
deliberately not reattached to some unrelated neighbor, since that would be more
misleading than losing them.
yaml.Marshal(today's behavior) if the base YAML doesn't parse - neverhard-fails an edit just because the base became unparseable.
Wiring:
configEditRequestgainsbase_yamlalongside the existingbase_hash- the clientmust resend whatever YAML text it last received as the merge base every time it submits a
structured
Config, since a validate call never touches disk and has no other way to know whatcomments to preserve. Each round trip's returned
yamlbecomes the next round's base, socomments survive arbitrarily many mode toggles within a session, not just a single save. The
save path itself merges onto the already-hash-verified on-disk bytes rather than trusting the
client-sent base, so it can never be stale or forged.
Net effect: comments are only ever edited by typing in the raw YAML textarea (structured-form
fields have nowhere to put a comment) - but they now survive being carried through a
structured-form edit session and back out again, instead of being destroyed the moment the
structured form is touched.
Branch:
issue34_preserve_comments.