a2ui-project / a2ui-project/a2ui

[BUG]: A malformed path deletes what it is written through in the Swift DataModel, and the shared suite does not run

Ouverte
#2,625 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub
status: needs-triage
Langage dominant
TypeScript
Étoiles
16.4k
Forks
1.3k
Merge moyen
2 j 13 h
PR mergées (30 j)
134

Description

- [x] I have searched the existing issues to make sure this bug has not already been reported.

## Describe the Bug

The Swift client does not run `conformance/core/data_model.yaml`. It has a hand-written `DataModelPointerConformanceTests` instead, while the Dart client and `web_core` are both driven by the shared suite (#2622 covers the Python client, which was in the same position).

Running the suite against `DataModel` executes 19 of its 37 cases. All 19 pass. The four that fail are the four that write a path through a value which is not a container, and each one destroys data:

| initial | write | result |
| --- | --- | --- |
| `/user/name` is `"Alice"` | `set("/user/name/first", "Bob")` | `{"name": {"first": "Bob"}}` — `"Alice"` is gone |
| `/items` is `["a","b","c"]` | `set("/items/0/foo", "bar")` | `[{"foo":"bar"},"b","c"]` — `"a"` is gone |
| `/items` is `["a","b","c"]` | `set("/items/foo", "bar")` | `{"foo":"bar"}` — **the whole array is gone** |
| `/items` is `["a","b","c"]` | `set("/items/foo/bar", "v")` | `{"foo":{"bar":"v"}}` — **the whole array is gone** |

The last two are the ones to worry about. A single non-numeric segment in a path an agent emits replaces an entire array with an object, and nothing is reported.

`JSONValue.update` auto-vivifies over anything that is not an object or an array, and its array branch builds an object when the key does not parse as an index — which returns in place of the array, rather than leaving it alone.

### It was pinned, which is why nothing caught it

```swift
@Test func subscriptSetAutoVivifiesArrayFromPrimitive() {
var value: JSONValue = "primitive"
value["0/name"] = "Alice"
#expect(value.arrayValue?.count == 1)
#expect(value["0/name"]?.stringValue == "Alice")
}
```

That test asserts the string is replaced. `test_data_model_rejects_write_through_primitive` in the shared suite says the same write is an error, and Dart, `web_core` and Python all reject it. A local unit test and the shared suite disagreed, and the local one was the only one running.

## Steps to Reproduce

```swift
let model = DataModel(initial: .object(["items": .array([.string("a"), .string("b")])]))
model.set("/items/foo", value: .string("bar"))
print(model.get("/items") as Any) // {"foo": "bar"} — both elements gone
```

## Expected Behavior

Auto-vivification fills in absent and null nodes only, and a path through anything else is rejected rather than clearing it.

## Two decisions this needs, which is why the PR only goes half way

The PR attached to this issue stops the destruction: those writes now leave the tree as it is. It deliberately does not make the four cases pass, because both of the remaining steps change public API and are yours to choose.

**1. Can `set` report a rejected write?** `public func set(_ path: String, value: JSONValue?)` returns Void, so a rejected write can only be dropped in silence. Dart's comment on the same branch says it out loud — "dropping the write would hide a malformed path" — and the suite agrees. Making Swift agree means `throws`, a `Result`, or an error publisher on `DataModel`. Which would you want?

**2. Is a per-path observer API wanted?** 14 of the suite's 37 cases attach an observer to a path and assert who was notified. `DataModel` exposes `dataPublisher`, which emits the whole tree, so there is nothing to attach to a single path and those cases cannot run at all. Dart has `watch(path)`, `web_core` and Python have `subscribe(path, ...)`.

Until both are settled, the harness in the PR skips those two shapes — by their shape, not by a list of names, so neither skip can quietly grow — and prints what it left out on every run.

## Environment Details

- **OS**: macOS 15
- **Browser/Platform**: Swift 6, `swift test --enable-swift-testing`
- **SDK/Package Name & Version**: `A2UISwiftCore` at `main`
- **Protocol Version**: v0.9

Guide de contribution

Ouvrir le guide de contribution

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.