Skip to content

Commit 73c143c

Browse files
author
gluck
committed
form: one projection, one constructor, and comments that say what code does
Answers four keel threads on bcfa3f6. ## the acrobatics Taking a patch, splitting it into dotted keys, and rebuilding it against an empty snapshot. Six sites did this to add one key. Patches compose with Merge: form.Merge(dress, form.Build(form.Snapshot{}, add, nil)) Outfit layering was the same shape at a larger scale: N patches decomposed to key maps, unioned, rebuilt. It is a fold of Merge. That rewrite also removed a silent bug. Leaves() returned a fresh map, so p.Leaves()[k] = v wrote to a temporary and was discarded. Five sites did it, including the aria_id and cwd stamps on every birth patch. ## the duplication removed Leaves, Keys, Removes, IsEmpty, Creates, creationsOnly, sortStrings, and four near-identical recursive walkers kept Entries, Entry, Build Entries walks the patch once and yields (path, before, after). Keys, removals and set values are filters over it. IsEmpty was an alias for IsIdentity; Creates was Build with an empty base. patch.go: 220 lines removed, 59 added. ## comments Removed the inline justifications, the capitals used for emphasis, and every reference to how the code used to be. The git history holds that.
1 parent bcfa3f6 commit 73c143c

130 files changed

Lines changed: 641 additions & 818 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎api/form/diff.go‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func diffList(a, b []KeyedValue) Patch {
9090
out.Delete = append(out.Delete, it)
9191
}
9292
}
93-
// The window is computed over the sequence AS APPLY WILL SEE IT: after
93+
// The window is computed over the sequence AS Apply will see IT: after
9494
// creates are appended and deletes removed. Both directions are computed
9595
// here because only here are both orders in hand.
9696
fwdFrom := intermediate(a, bi, out.Create)
@@ -136,13 +136,11 @@ func keysOf(items []KeyedValue) []string {
136136
return out
137137
}
138138

139-
// windowFor is the PARTIAL WINDOW transforming from into to: the positions
140-
// where they disagree, widened by one neighbour either side as an anchor.
139+
// windowFor is the window transforming from into to: the positions where they
140+
// disagree, widened by one neighbour either side as an anchor.
141141
//
142-
// The scan is linear, so moving one item a long way marks every key it passed
143-
// as moved -- those keys really are in different positions relative to the
144-
// survivors, and a scan cannot know which single move explains it. A known and
145-
// accepted approximation.
142+
// The scan is linear, so moving one item far marks every key it passed as
143+
// moved. A known approximation.
146144
func windowFor(from, to []string) *Order {
147145
if len(from) != len(to) {
148146
return nil

‎api/form/environment.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,5 @@ func EnvironmentPatch() Patch {
5454
if set == nil {
5555
return Patch{}
5656
}
57-
return Creates(set)
57+
return Build(Snapshot{}, set, nil)
5858
}

‎api/form/fold_test.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func oldRenderLoop(s Snapshot, patches []Patch, tmpls *template.Template) ([]str
4747
func foldCorpus(t *testing.T) [][]Patch {
4848
t.Helper()
4949
set := func(kv ...string) Patch {
50-
p := Creates(map[string]json.RawMessage{})
50+
p := Build(Snapshot{}, map[string]json.RawMessage{}, nil)
5151
for i := 0; i+1 < len(kv); i += 2 {
5252
b, err := json.Marshal(kv[i+1])
5353
if err != nil {
@@ -174,8 +174,8 @@ func TestFoldRenderSeesTheBoardBeforeEachPatch(t *testing.T) {
174174
tmpls := orderSensitiveTemplates(t)
175175
board := FromMap(map[string]json.RawMessage{"mantra": json.RawMessage(`"first"`)})
176176
patches := []Patch{
177-
Creates(map[string]json.RawMessage{"mantra": json.RawMessage(`"second"`)}),
178-
Creates(map[string]json.RawMessage{"mantra": json.RawMessage(`"third"`)}),
177+
Build(Snapshot{}, map[string]json.RawMessage{"mantra": json.RawMessage(`"second"`)}, nil),
178+
Build(Snapshot{}, map[string]json.RawMessage{"mantra": json.RawMessage(`"third"`)}, nil),
179179
}
180180
var bodies []string
181181
final := FoldRender(board, patches, tmpls,

‎api/form/form.go‎

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
// Snapshot is a structural view of an aria's board: a JSON object whose
1313
// members are the keys, nested where the keys nest.
1414
//
15-
// It was a FLAT map of dotted strings to opaque JSON, which made a dot a
1615
// convention nothing enforced and made every value below the first level
1716
// unreachable: changing one field of a 6.5KB credo rewrote the credo. The
1817
// dots address a tree now, and a patch reaches any node of it.
@@ -21,7 +20,6 @@ type Snapshot struct {
2120
}
2221

2322
// Path is a key resolved to its segments. Dots address the tree; a segment
24-
// may itself contain a dot where the key was written that way, so a path is
2523
// resolved AGAINST a snapshot rather than parsed in isolation.
2624
type Path []string
2725

@@ -51,11 +49,8 @@ func (s Snapshot) Root() Value {
5149
// FromValue wraps a root object as a snapshot.
5250
func FromValue(v Value) Snapshot { return Snapshot{root: v} }
5351

54-
// Get returns the raw value at key and whether it was present.
55-
//
56-
// The key is resolved segment by segment, LONGEST MATCH FIRST at every level,
52+
// The key is resolved segment by segment, Longest match first at every level,
5753
// so a key whose own name contains a dot resolves to itself rather than to a
58-
// path that does not exist. That is what makes `skills.howto` and
5954
// `skills.howto.md` both addressable.
6055
func (s Snapshot) Get(key string) (json.RawMessage, bool) {
6156
v, ok := s.resolve(key)
@@ -187,11 +182,15 @@ func (s Snapshot) Apply(p Patch) Snapshot {
187182
return Snapshot{root: next}
188183
}
189184

190-
// Additive keeps only what p would actually change on s. A patch that removes
191-
// is not additive, so removals are dropped and what remains is re-diffed
192-
// against s: whatever is left is genuinely new or genuinely different.
185+
// Additive keeps only what p would add to s. Removals are dropped.
193186
func Additive(s Snapshot, p Patch) Patch {
194-
return s.Apply(p.creationsOnly()).Diff(s)
187+
set := map[string]json.RawMessage{}
188+
for _, e := range p.Entries() {
189+
if !e.IsRemoval() {
190+
set[e.Key] = e.New
191+
}
192+
}
193+
return Build(s, set, nil)
195194
}
196195

197196
// SetPath returns a snapshot with key set to v, creating intermediate objects.
@@ -287,7 +286,6 @@ func decodeRawObject(v Value) (map[string]json.RawMessage, bool) {
287286
// MarshalJSON emits the nested object: what the form channel holds on disk.
288287
func (s Snapshot) MarshalJSON() ([]byte, error) { return s.Root().Raw(), nil }
289288

290-
// UnmarshalJSON reads the nested object. A FLAT board written before the tree
291289
// -- dotted keys at the top level -- is nested on read, so an old store opens
292290
// without a rewrite.
293291
func (s *Snapshot) UnmarshalJSON(data []byte) error {
@@ -330,8 +328,8 @@ func (e Entry) NewString() string { return decodeStringOrRaw(e.New) }
330328
// OldString is the entry's old value as a string, or "".
331329
func (e Entry) OldString() string { return decodeStringOrRaw(e.Old) }
332330

333-
// PatchEntries flattens a patch to one entry per changed LEAF, against the
334-
// board as it stood before.
331+
// PatchEntries is the patch's entries resolved against the board it applies
332+
// to, so an entry that changes an existing key carries the value it replaces.
335333
func PatchEntries(p Patch, prev Snapshot) []Entry {
336334
next := prev.Apply(p)
337335
before := map[string]json.RawMessage{}

‎api/form/form_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestDiff_NoChange(t *testing.T) {
5151
"model": raw(t, "claude-opus-4-6"),
5252
})
5353
p := next.Diff(prev)
54-
assert.True(t, p.IsEmpty(), "identical snapshots produce empty patch")
54+
assert.True(t, p.IsIdentity(), "identical snapshots produce empty patch")
5555
}
5656

5757
func TestDiff_AddSetRemove(t *testing.T) {

‎api/form/incantation_test.go‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ func TestStudyIncantationReadsEveryEvent(t *testing.T) {
4040
if got.OnStudy != "watch it" || got.OnUpdate != "it moved" || got.OnDrop != "look away" {
4141
t.Fatalf("all three events should carry their phrase, got %+v", got)
4242
}
43-
if got.IsEmpty() {
43+
if got.IsIdentity() {
4444
t.Fatal("a populated incantation reports itself empty")
4545
}
4646
}
4747

4848
func TestStudyIncantationAbsentIsSilentAndEmpty(t *testing.T) {
4949
out := captureWarnings(t, func() {
50-
if got := ReadStudyIncantation(Snapshot{}); !got.IsEmpty() {
50+
if got := ReadStudyIncantation(Snapshot{}); !got.IsIdentity() {
5151
t.Fatalf("no key should read as no incantation, got %+v", got)
5252
}
5353
})
@@ -85,7 +85,7 @@ func TestStudyIncantationWrongShapeIsRefusedWholesale(t *testing.T) {
8585
out := captureWarnings(t, func() {
8686
got = ReadStudyIncantation(boardWith(t, StudyIncantationKey, tc.raw))
8787
})
88-
if !got.IsEmpty() {
88+
if !got.IsIdentity() {
8989
t.Fatalf("a %s must not parse as an incantation: %+v", tc.name, got)
9090
}
9191
if !strings.Contains(out, tc.kind) {
@@ -104,7 +104,7 @@ func TestStudyIncantationNamesUnknownKeys(t *testing.T) {
104104
board := boardWith(t, StudyIncantationKey, `{"onstudied":"watch it"}`)
105105
var got StudyIncantation
106106
out := captureWarnings(t, func() { got = ReadStudyIncantation(board) })
107-
if !got.IsEmpty() {
107+
if !got.IsIdentity() {
108108
t.Fatalf("an unknown field must not become a phrase: %+v", got)
109109
}
110110
if !strings.Contains(out, "onstudied") || !strings.Contains(out, "onstudy") {
@@ -125,7 +125,7 @@ func TestForkIncantationWrongShapeIsRefused(t *testing.T) {
125125
out := captureWarnings(t, func() {
126126
got = ReadForkIncantation(boardWith(t, ForkIncantationKey, `["a","b"]`))
127127
})
128-
if !got.IsEmpty() {
128+
if !got.IsIdentity() {
129129
t.Fatalf("an array must not parse: %+v", got)
130130
}
131131
if !strings.Contains(out, "array") {
@@ -137,7 +137,7 @@ func TestForkIncantationWrongShapeIsRefused(t *testing.T) {
137137
// says less than nothing: it tells the model there was something to say.
138138
func TestIncantationTrimsAndDropsBlank(t *testing.T) {
139139
s := ReadStudyIncantation(boardWith(t, StudyIncantationKey, `{"onstudy":" "}`))
140-
if !s.IsEmpty() {
140+
if !s.IsIdentity() {
141141
t.Fatalf("a blank phrase must be no phrase: %+v", s)
142142
}
143143
f := ReadForkIncantation(boardWith(t, ForkIncantationKey, `" branched "`))

‎api/form/legacy_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func TestLegacyFlatPatchStillApplies(t *testing.T) {
1515
raw := []byte(`{"set":{"system.model":"claude-opus-5","mantra":"hello","skills.howto":{"a":1}},"remove":["stale"]}`)
1616
var p form.Patch
1717
require.NoError(t, json.Unmarshal(raw, &p))
18-
require.False(t, p.IsEmpty(), "a flat patch decoded to Identity: history would vanish")
18+
require.False(t, p.IsIdentity(), "a flat patch decoded to Identity: history would vanish")
1919

2020
base := form.FromMap(map[string]json.RawMessage{"stale": json.RawMessage(`"x"`)})
2121
got := base.Apply(p)

0 commit comments

Comments
 (0)