MCP update_item löscht still den checklist-Render-Hint eines Containers (Datenverlust, vorbestehend) #9

Open
opened 2026-07-17 10:05:52 +00:00 by mAi · 1 comment
Collaborator

Problem

mcp updateItemTool befüllt patch.Render nie aus dem bestehenden Item. Jeder mcp__projax__update_item-Aufruf schreibt das Item also mit leerem Render-Hint zurück — der metadata.projax.render = "checklist"-Hint eines Containers geht dabei still verloren, auch wenn der Aufruf mit dem Feld überhaupt nichts zu tun hatte (z.B. nur status setzen).

Gefunden von apollo beim Umsetzen von #7 (aliases-Write-Path). Nicht dort verursacht — vorbestehend. apollo hat es sauber gemeldet statt es nebenbei mitzufixen, weil es außerhalb des #7-Scopes lag. Richtige Entscheidung.

Warum das zählt

Der Checklist-Render-Modus ist Q1 aus dem Phase-7-Entity-Model: m hat sich bewusst gegen einen eigenen tasklist-Typ und für genau diesen einen Hint entschieden (docs/plans/phase-7-entity-model.md, "ONE container concept + metadata.projax.render=checklist"). Der Hint IST das Feature. Ein update_item über MCP macht aus einer Checkliste also wieder eine gewöhnliche Liste — lautlos, ohne Fehler, ohne dass jemand es merkt, bis m die Seite ansieht.

Das ist dieselbe Klasse wie #8: stiller Datenverlust auf einem Write-Pfad, der Erfolg meldet.

Fix

updateItemTool muss patch.Render aus dem geladenen Item vorbelegen, bevor es den Patch anwendet — so wie es das mit den anderen erhaltenswerten Feldern tut. Partial-Update heißt: was nicht mitgeschickt wird, bleibt stehen.

Beim Aufräumen mitprüfen, ob weitere Felder dieselbe Lücke haben — das Muster "Feld wird beim Partial-Update nicht vorbelegt und damit genullt" ist selten ein Einzelfall. Ein Test, der ein Item mit gesetztem Render-Hint über update_item nur im status ändert und danach den Hint noch vorfindet, pinnt das dauerhaft fest.

Refs

  • docs/plans/phase-7-entity-model.md §Q1 — warum der Hint existiert
  • #8 — dieselbe Klasse (stiller Verlust auf dem Write-Pfad)
  • Gefunden während #7 (apollo), nicht davon verursacht
## Problem `mcp updateItemTool` befüllt `patch.Render` nie aus dem bestehenden Item. Jeder `mcp__projax__update_item`-Aufruf schreibt das Item also mit leerem Render-Hint zurück — **der `metadata.projax.render = "checklist"`-Hint eines Containers geht dabei still verloren**, auch wenn der Aufruf mit dem Feld überhaupt nichts zu tun hatte (z.B. nur `status` setzen). Gefunden von `apollo` beim Umsetzen von #7 (aliases-Write-Path). **Nicht** dort verursacht — vorbestehend. apollo hat es sauber gemeldet statt es nebenbei mitzufixen, weil es außerhalb des #7-Scopes lag. Richtige Entscheidung. ## Warum das zählt Der Checklist-Render-Modus ist Q1 aus dem Phase-7-Entity-Model: m hat sich bewusst gegen einen eigenen `tasklist`-Typ und **für** genau diesen einen Hint entschieden (`docs/plans/phase-7-entity-model.md`, "ONE container concept + metadata.projax.render=checklist"). Der Hint IST das Feature. Ein `update_item` über MCP macht aus einer Checkliste also wieder eine gewöhnliche Liste — lautlos, ohne Fehler, ohne dass jemand es merkt, bis m die Seite ansieht. Das ist dieselbe Klasse wie #8: **stiller Datenverlust auf einem Write-Pfad, der Erfolg meldet.** ## Fix `updateItemTool` muss `patch.Render` aus dem geladenen Item vorbelegen, bevor es den Patch anwendet — so wie es das mit den anderen erhaltenswerten Feldern tut. Partial-Update heißt: was nicht mitgeschickt wird, bleibt stehen. Beim Aufräumen mitprüfen, **ob weitere Felder dieselbe Lücke haben** — das Muster "Feld wird beim Partial-Update nicht vorbelegt und damit genullt" ist selten ein Einzelfall. Ein Test, der ein Item mit gesetztem Render-Hint über `update_item` nur im `status` ändert und danach den Hint noch vorfindet, pinnt das dauerhaft fest. ## Refs - `docs/plans/phase-7-entity-model.md` §Q1 — warum der Hint existiert - #8 — dieselbe Klasse (stiller Verlust auf dem Write-Pfad) - Gefunden während #7 (apollo), nicht davon verursacht
mAi self-assigned this 2026-07-17 10:05:52 +00:00
Author
Collaborator

Gefixt — und es war kein Einzelfall

Commit: a48ef4396a
Branch: mai/hades/issue-9-mcp-update-item

Der gemeldete Bug stimmt exakt wie beschrieben. store.UpdateInput ist full-replace — jedes Feld, das ein Caller nicht aus dem geladenen Item vorbelegt, wird beim Write genullt. Der Auftrag "prüf mit, ob weitere Felder dieselbe Lücke haben" war berechtigt: drei Call-Sites, dasselbe Muster.

Was gefunden wurde

Ort Fehlendes Feld Status
mcp/tools.go updateItemTool Render, StartTime, EndTime live — der gemeldete Bug
web/bulk.go updateInputFromItem Render live — jede Bulk-Aktion fraß den Hint genauso
web/server.go handleDetailWrite StartTime, EndTime latent (s.u.)

web/bulk.go ist der interessante Fund: die Funktion projiziert StartTime/EndTime sauber durch, aber Render wurde beim Phase-7-Anbau schlicht vergessen. Ein Bulk-Tag- oder -Status-Update über /admin/bulk hat den Checklist-Hint also genauso still gelöscht wie MCP — derselbe Datenverlust, zweiter Pfad, unabhängig davon ob MCP je aufgerufen wird.

Live vs. latent — ehrlich getrennt

  • Render ist echter, aktiver Datenverlust. Die Web-Edit-Form setzt den Hint (web/server.go:775, Checkbox render_checklist), also existiert er real in metadata.projax.render und beide Write-Pfade haben ihn gefressen.
  • StartTime/EndTime sind heute latent. Kein projax-Pfad setzt sie derzeit (die Create-Pfade führen sie nicht), also ist praktisch überall nil — es ging bisher nichts kaputt. Aber: der mBrian-Reader entpackt sie aus metadata.projax.start_time, und timePtrToJSON(nil)null → der PATCH-Shallow-Merge löscht sie. Sobald irgendwas außerhalb projax einen Anchor setzt, ist es exakt derselbe Bug. Als Klasse mitgefixt statt als nächste Instanz stehen zu lassen.

Zusätzlicher Fund: der Hint war über MCP unsichtbar

itemView hatte gar kein render-Feld — get_item/update_item haben den Hint nie zurückgegeben. Kein MCP-Client konnte also sehen, ob ein Container eine Checkliste ist, geschweige denn merken, dass der Hint verschwindet. Das erklärt mit, warum der Bug so lange still bleiben konnte. Jetzt wird render im View emittiert — read-only, die Web-Form bleibt der einzige Writer.

Tests

Die DB-Tests (mustDBServer) laufen gegen den Legacy-*store.Store, der für Render keine Spalte hat und das Feld wegwirft — sie können diesen Bug prinzipiell nicht fangen. Deshalb ein fakeStore (in-memory Reader+Writer), der den an den Writer übergebenen store.UpdateInput mitschreibt. Der pinnt den Contract auf der Ebene, die ihn tatsächlich besitzt, und läuft ohne DB.

  • TestUpdateItem_PreservesRenderHint — genau der geforderte Test: nur status ändern, Hint muss stehen bleiben
  • TestUpdateItem_PreservesUnsentFields — die ganze Klasse (Render/Start/End + Titel/Tags/Aliases/Public/…)
  • TestUpdateItem_ReportsRenderHint — Gegenrichtung: Preserve darf nicht Erfinden werden (ein Item ohne Hint bekommt keinen)
  • TestGetItem_ExposesRenderHint — Read-Seite bleibt ehrlich

Gegen den ungefixten Code verifiziert — alle Assertions schlagen fehl (got "", want "checklist"; StartTime: got <nil>). Ein Regressionstest, der auch ohne Fix grün ist, pinnt nichts.

Testlage

go build ./... und go vet sauber, mcp/ grün. Vorbestehende Failures in web/ (9), store/ (3) und db/ (1) sind unverändert — vor und nach dem Change identisch, per git stash gegengeprüft. Nicht von mir und nicht von mir angefasst.

Eine offene Frage für m

render ist jetzt preserve-only + read-only über MCP — die Web-Checkbox bleibt der einzige Writer. Ob update_item den Hint auch setzen können soll, ist eine Design-Entscheidung (Q1-Territorium), keine Bug-Fix-Frage — deshalb nicht eigenmächtig entschieden. Sag Bescheid, falls gewünscht; ist ein Einzeiler plus Schema-Feld.


Nachtrag zur Meldung: apollo hat richtig gehandelt, den Fund aus #7 rauszuhalten. Der Fix hier berührt drei Dateien und zwei Pfade, die mit Aliases nichts zu tun haben — als Beifang in #7 wäre genau der web/bulk.go-Fund untergegangen.

## Gefixt — und es war kein Einzelfall Commit: https://mgit.msbls.de/m/projax/commit/a48ef4396a97c4ba0e07dbb8e2bd21e2b8de5adb Branch: `mai/hades/issue-9-mcp-update-item` Der gemeldete Bug stimmt exakt wie beschrieben. `store.UpdateInput` ist **full-replace** — jedes Feld, das ein Caller nicht aus dem geladenen Item vorbelegt, wird beim Write genullt. Der Auftrag "prüf mit, ob weitere Felder dieselbe Lücke haben" war berechtigt: **drei Call-Sites, dasselbe Muster.** ### Was gefunden wurde | Ort | Fehlendes Feld | Status | |---|---|---| | `mcp/tools.go` `updateItemTool` | `Render`, `StartTime`, `EndTime` | **live** — der gemeldete Bug | | `web/bulk.go` `updateInputFromItem` | `Render` | **live** — jede Bulk-Aktion fraß den Hint genauso | | `web/server.go` `handleDetailWrite` | `StartTime`, `EndTime` | latent (s.u.) | `web/bulk.go` ist der interessante Fund: die Funktion projiziert `StartTime`/`EndTime` sauber durch, aber `Render` wurde beim Phase-7-Anbau schlicht vergessen. Ein Bulk-Tag- oder -Status-Update über `/admin/bulk` hat den Checklist-Hint also **genauso still gelöscht** wie MCP — derselbe Datenverlust, zweiter Pfad, unabhängig davon ob MCP je aufgerufen wird. ### Live vs. latent — ehrlich getrennt - **`Render` ist echter, aktiver Datenverlust.** Die Web-Edit-Form setzt den Hint (`web/server.go:775`, Checkbox `render_checklist`), also existiert er real in `metadata.projax.render` und beide Write-Pfade haben ihn gefressen. - **`StartTime`/`EndTime` sind heute latent.** Kein projax-Pfad *setzt* sie derzeit (die Create-Pfade führen sie nicht), also ist praktisch überall `nil` — es ging bisher nichts kaputt. Aber: der mBrian-Reader entpackt sie aus `metadata.projax.start_time`, und `timePtrToJSON(nil)` → `null` → der PATCH-Shallow-Merge löscht sie. Sobald irgendwas außerhalb projax einen Anchor setzt, ist es exakt derselbe Bug. Als Klasse mitgefixt statt als nächste Instanz stehen zu lassen. ### Zusätzlicher Fund: der Hint war über MCP unsichtbar `itemView` hatte **gar kein** `render`-Feld — `get_item`/`update_item` haben den Hint nie zurückgegeben. Kein MCP-Client konnte also sehen, ob ein Container eine Checkliste ist, geschweige denn merken, dass der Hint verschwindet. Das erklärt mit, warum der Bug so lange still bleiben konnte. Jetzt wird `render` im View emittiert — **read-only**, die Web-Form bleibt der einzige Writer. ### Tests Die DB-Tests (`mustDBServer`) laufen gegen den Legacy-`*store.Store`, der für `Render` **keine Spalte hat und das Feld wegwirft** — sie können diesen Bug prinzipiell nicht fangen. Deshalb ein `fakeStore` (in-memory Reader+Writer), der den an den Writer übergebenen `store.UpdateInput` mitschreibt. Der pinnt den Contract auf der Ebene, die ihn tatsächlich besitzt, und läuft ohne DB. - `TestUpdateItem_PreservesRenderHint` — genau der geforderte Test: nur `status` ändern, Hint muss stehen bleiben - `TestUpdateItem_PreservesUnsentFields` — die ganze Klasse (Render/Start/End + Titel/Tags/Aliases/Public/…) - `TestUpdateItem_ReportsRenderHint` — Gegenrichtung: Preserve darf nicht Erfinden werden (ein Item ohne Hint bekommt keinen) - `TestGetItem_ExposesRenderHint` — Read-Seite bleibt ehrlich **Gegen den ungefixten Code verifiziert** — alle Assertions schlagen fehl (`got "", want "checklist"`; `StartTime: got <nil>`). Ein Regressionstest, der auch ohne Fix grün ist, pinnt nichts. ### Testlage `go build ./...` und `go vet` sauber, `mcp/` grün. Vorbestehende Failures in `web/` (9), `store/` (3) und `db/` (1) sind **unverändert** — vor und nach dem Change identisch, per `git stash` gegengeprüft. Nicht von mir und nicht von mir angefasst. ### Eine offene Frage für m `render` ist jetzt preserve-only + read-only über MCP — die Web-Checkbox bleibt der einzige Writer. Ob `update_item` den Hint auch **setzen** können soll, ist eine Design-Entscheidung (Q1-Territorium), keine Bug-Fix-Frage — deshalb nicht eigenmächtig entschieden. Sag Bescheid, falls gewünscht; ist ein Einzeiler plus Schema-Feld. --- *Nachtrag zur Meldung: apollo hat richtig gehandelt, den Fund aus #7 rauszuhalten. Der Fix hier berührt drei Dateien und zwei Pfade, die mit Aliases nichts zu tun haben — als Beifang in #7 wäre genau der `web/bulk.go`-Fund untergegangen.*
mAi added the
status:done
label 2026-07-17 10:14:32 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: m/projax#9
No description provided.