Der zweite Weg ist weg -- nicht nur sein Knopf

VanVan im Support, Meldung #8: „Wenn man auf ich fange an drueckt
steht dort in Bearbeitung und wenn man auf fertig drueckt dann wird
es zu erledigt. DIE AUFGABE BLEIBT ABER IM STATUS OFFEN STEHEN."

GESTERN HABE ICH DIE HALBE ARBEIT GEMACHT und daneben eine Ausrede
geschrieben. Die zwei Knoepfe kamen weg, und in den Kommentar kam:

    „Der Weg `/mein-stand` bleibt bestehen -- er ist die Schranke,
     falls ihn jemand direkt anspricht."

Eine Route ist keine Schranke gegen sich selbst. Sie setzte
weiterhin NUR `aufgaben_zuteilung.zustand` und liess
`aufgaben.status` stehen -- also genau den Widerspruch, den VanVan
beschrieben hat. Ich hatte ihn unsichtbar gemacht, nicht
abgeschafft: kein Knopf mehr, das Verhalten unveraendert im System.

GEFUNDEN BEIM NACHMESSEN AM LAUFENDEN SERVER, nicht beim Schreiben.
Filipe hat auf den Screenshot gezeigt und gesagt, es sei noch nicht
in Ordnung. Statt meine Pruefungen zu zitieren habe ich die Route
gelesen -- und dort stand es.

NACHGEMESSEN, BEVOR SIE WEGKAM: Kein einziger Aufruf mehr im
ausgelieferten Browsercode (grep ueber alle JS- und HTML-Dateien des
Workspace). Nur zwei Pruefungen benutzten sie.

UND EINE DAVON NICKTE DEN FEHLER AB. In pruef-zuteilung stand:

    Bea setzt "in Bearbeitung" (HTTP 200)
    und danach "erledigt" (HTTP 200)

Zwei gruene Haken ueber genau dem Verhalten, das gemeldet wurde --
weil sie nur den Rueckgabewert ansahen und nie den Aufgabenstatus
daneben. Eine Pruefung, die nur eine Haelfte misst, kann den
Widerspruch gar nicht finden. Jetzt steht dort:

    Bea setzt "in Bearbeitung" (HTTP 200)
      und BEIDES steht auf "in Arbeit" (Aufgabe arbeit,
      Zuteilung arbeit) — das war VanVans Befund
    und danach "erledigt" (HTTP 200)
      und wieder beides (Aufgabe erledigt, Zuteilung erledigt)

WAS JETZT GILT: `PATCH /workspace/api/aufgaben/:id` mit `{ status }`.
Er setzt den Status UND zieht die Zuteilung mit
(`zuteilungenNachStatus`), kennt dieselbe Sperre fuer dauerhafte
Aufgaben und dieselbe Rechtepruefung. Eine Frage, eine Antwort.

ENTFERNT STATT AUSKOMMENTIERT -- dieselbe Entscheidung wie bei
`/vorlagen/hilfe` am 01.09.: Eine Route, die niemand mehr aufruft,
wird beim naechsten Mal fuer lebenden Code gehalten und mitgepflegt.

EIN SCHRECKMOMENT UNTERWEGS, der sich als Messfehler herausstellte:
Nach der Umstellung meldete pruef-bewerbung-aufgaben eine 404 beim
Abhaken -- also der Verdacht, dass eine ZUGETEILTE Aufgabe ueber den
Statusweg gar nicht erreichbar ist und ich gerade etwas kaputt
gemacht haette. Nachgemessen statt geglaubt: Die Aufgabe steht in
ihrer Liste, der PATCH antwortet 200. Die rote Zeile war eine
DRITTE Stelle, die ich beim Umstellen uebersehen hatte und die noch
auf die alte Route zeigte. Zwei Minuten Messung statt einer Stunde
Suche an der falschen Stelle.

GEPRUEFT

  pruef-zuteilung             ok, mit zwei neuen Zeilen, die BEIDE
                              Zustaende messen
    den zweiten Weg (mein-stand) gibt es nicht mehr (404)
  pruef-bewerbung-aufgaben    178 ok
  pruef-struktur              102 ok, 414 -> 413 Routen
  pruef-aufgabenbrett · pruef-modi-katalog 159 ·
  pruef-aufgaben-vorlagen 60

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-03 11:31:18 +02:00
co-authored by Claude Opus 5
parent 2270f0de91
commit ef911691b7
50 changed files with 779 additions and 753 deletions
+14 -9
View File
@@ -1136,17 +1136,22 @@ melde("=== 6. Am echten Bildschirm ===");
/* DER KERN DER MELDUNG. */
await roh(`/workspace/api/aufgaben/${dId}/annehmen`,
{ methode: "POST", keks: modi.keks });
const fertig = await roh(`/workspace/api/aufgaben/${dId}/mein-stand`, {
methode: "POST", keks: modi.keks, body: { zustand: "erledigt" } });
/* UEBER DEN STATUS, nicht ueber `/mein-stand` (03.10.2026).
Den zweiten Weg gibt es nicht mehr: Er setzte den
Zuteilungszustand, ohne den Aufgabenstatus mitzuziehen --
VanVans Meldung #8. Geprueft wird jetzt die Tuer, die es
noch gibt. */
const fertig = await roh(`/workspace/api/aufgaben/${dId}`, {
methode: "PATCH", keks: modi.keks, body: { status: "erledigt" } });
ok(fertig.code === 409 && fertig.json?.fehler === "dauerhafte_aufgabe",
`Frida kann sie nicht abhaken (${fertig.code}/${fertig.json?.fehler})`);
/* GEGENPROBE 1: „Ich fange an" geht weiterhin. Auch eine
stehende Aufgabe hat einen Anfang -- gesperrt ist nur das
Abhaken, nicht die Arbeit. */
const anfang = await roh(`/workspace/api/aufgaben/${dId}/mein-stand`, {
methode: "POST", keks: modi.keks, body: { zustand: "arbeit" } });
ok(anfang.code === 200, `„Ich fange an" geht trotzdem (${anfang.code})`);
const anfang = await roh(`/workspace/api/aufgaben/${dId}`, {
methode: "PATCH", keks: modi.keks, body: { status: "arbeit" } });
ok(anfang.code === 200, `anfangen geht trotzdem (${anfang.code})`);
/* GEGENPROBE 2: Bei einer GEWOEHNLICHEN Aufgabe geht das Abhaken.
Ohne sie hiesse „409" womoeglich nur, dass niemand je etwas
@@ -1158,8 +1163,8 @@ melde("=== 6. Am echten Bildschirm ===");
const nId = normal.json?.aufgabe?.id ?? normal.json?.id;
await roh(`/workspace/api/aufgaben/${nId}/annehmen`,
{ methode: "POST", keks: modi.keks });
const ok2 = await roh(`/workspace/api/aufgaben/${nId}/mein-stand`, {
methode: "POST", keks: modi.keks, body: { zustand: "erledigt" } });
const ok2 = await roh(`/workspace/api/aufgaben/${nId}`, {
methode: "PATCH", keks: modi.keks, body: { status: "erledigt" } });
ok(ok2.code === 200,
`eine gewoehnliche Aufgabe haekt sie sehr wohl ab (${ok2.code})`);
@@ -1193,8 +1198,8 @@ melde("=== 6. Am echten Bildschirm ===");
const a3 = await lesen(hand.keks);
ok(beenden.code === 200 && !a3?.dauerhaft,
`die rechte Hand beendet sie (${beenden.code}, danach ${a3?.dauerhaft})`);
const jetztFertig = await roh(`/workspace/api/aufgaben/${dId}/mein-stand`, {
methode: "POST", keks: modi.keks, body: { zustand: "erledigt" } });
const jetztFertig = await roh(`/workspace/api/aufgaben/${dId}`, {
methode: "PATCH", keks: modi.keks, body: { status: "erledigt" } });
ok(jetztFertig.code === 200,
`und danach laesst sie sich abhaken (${jetztFertig.code})`);
}
+42 -10
View File
@@ -458,15 +458,21 @@ melde("\n=== Fertig machen und bewerten ===");
`eine dauerhafte Aufgabe (#${idDauer})`);
await rufe(`/workspace/api/aufgaben/${idDauer}/annehmen`, { method: "POST", keks: anna });
const ueberStand = await rufe(`/workspace/api/aufgaben/${idDauer}/mein-stand`,
/* ES GIBT NUR NOCH EINE TUER (03.10.2026). Hier standen zwei:
`/mein-stand` und der Status. Die erste ist weg -- sie setzte
den Zuteilungszustand, ohne den Aufgabenstatus mitzuziehen,
und war damit der zweite Zustand, den VanVans Meldung #8
benannt hat. Dass sie zu war, hat nichts geheilt; dass es sie
nicht mehr gibt, schon. */
const weg = await rufe(`/workspace/api/aufgaben/${idDauer}/mein-stand`,
{ method: "POST", keks: anna, body: { zustand: "erledigt" } });
ok(ueberStand.code === 409 && ueberStand.json?.fehler === "dauerhafte_aufgabe",
`Tuer 1 (mein-stand) ist zu (${ueberStand.code} ${ueberStand.json?.fehler})`);
ok(weg.code === 404,
`den zweiten Weg (mein-stand) gibt es nicht mehr (${weg.code})`);
const ueberStatus = await rufe(`/workspace/api/aufgaben/${idDauer}`,
{ method: "PATCH", keks: anna, body: { status: "erledigt" } });
ok(ueberStatus.code === 409 && ueberStatus.json?.fehler === "dauerhafte_aufgabe",
`Tuer 2 (Status) jetzt auch (${ueberStatus.code} ${ueberStatus.json?.fehler})`
`und der Status ist zu (${ueberStatus.code} ${ueberStatus.json?.fehler})`
+ " — das war VanVans Befund");
/* ANFANGEN DARF SIE SEHR WOHL. Auch eine stehende Aufgabe hat
@@ -507,14 +513,40 @@ melde("\n=== Fertig machen und bewerten ===");
ok(wiederDa === "angenommen",
` zurueckgedreht steht Anna wieder auf „angenommen" (${wiederDa})`);
const arbeit = await rufe(`/workspace/api/aufgaben/${idPool}/mein-stand`, { method: "POST", keks: bea,
body: { zustand: "arbeit" } });
/* DER WEG BLEIBT BESTEHEN, auch ohne Knopf: Er ist die Schranke
fuer den, der die Schnittstelle direkt anspricht. */
/* ==== BEIDE ZUSTAENDE, NICHT NUR HTTP 200 (03.10.2026) ==========
Hier stand der Weg ueber `/mein-stand` und daneben nur die
Frage, ob er mit 200 antwortet. GENAU DAS hat VanVans Fehler
durchgewunken: Die Zuteilung sprang auf „in Bearbeitung", der
Aufgabenstatus blieb auf „offen" -- und die Pruefung sagte ok,
weil sie ihn nie angesehen hat.
Jetzt geht es ueber den Status, und gemessen werden BEIDE
Seiten. Eine Pruefung, die nur eine Haelfte ansieht, kann den
Widerspruch gar nicht finden. */
const standVonBea = () => {
const x = new DatabaseSync(process.env.WORKSPACE_DB);
const a = x.prepare("SELECT status FROM aufgaben WHERE id = ?").get(idPool);
const z = x.prepare(
"SELECT zustand FROM aufgaben_zuteilung WHERE aufgabe_id = ? AND person_id = ?")
.get(idPool, idBea);
x.close();
return { status: a?.status, zustand: z?.zustand };
};
const arbeit = await rufe(`/workspace/api/aufgaben/${idPool}`, { method: "PATCH", keks: bea,
body: { status: "arbeit" } });
const beideArbeit = standVonBea();
ok(arbeit.code === 200, `Bea setzt "in Bearbeitung" (HTTP ${arbeit.code})`);
const fertig = await rufe(`/workspace/api/aufgaben/${idPool}/mein-stand`, { method: "POST", keks: bea,
body: { zustand: "erledigt" } });
ok(beideArbeit.status === "arbeit" && beideArbeit.zustand === "arbeit",
` und BEIDES steht auf "in Arbeit" (Aufgabe ${beideArbeit.status}, `
+ `Zuteilung ${beideArbeit.zustand}) — das war VanVans Befund`);
const fertig = await rufe(`/workspace/api/aufgaben/${idPool}`, { method: "PATCH", keks: bea,
body: { status: "erledigt" } });
const beideFertig = standVonBea();
ok(fertig.code === 200, `und danach "erledigt" (HTTP ${fertig.code})`);
ok(beideFertig.status === "erledigt" && beideFertig.zustand === "erledigt",
` und wieder beides (Aufgabe ${beideFertig.status}, Zuteilung ${beideFertig.zustand})`);
/* DIE DREI RUECKMELDUNGEN AUS ABSCHNITT 5. */
const leer = await rufe(`/workspace/api/aufgaben/${idPool}/bewerten`, { method: "POST", keks: chef,
+25 -48
View File
@@ -760,61 +760,38 @@ for (const [weg, entscheidung] of [["annehmen", "ja"], ["ablehnen", "nein"]]) {
});
}
/* ---- Meinen eigenen Stand setzen ------------------------------------ */
zuteilungRouter.post("/workspace/api/aufgaben/:id/mein-stand", (req, res) => {
try {
const a = holen(req, res); if (!a) return;
const z = meine(a.id, req.person.id);
if (!z) return res.status(403).json({ fehler: "nicht_zugeteilt" });
/* ==== HIER STAND `/mein-stand` (03.10.2026 entfernt) ==============
const ziel = String(req.body?.zustand ?? "").trim();
/* NUR DIE DREI, DIE MAN SELBST SETZEN DARF. "offen" zurückzudrehen
hieße, die eigene Zusage zu löschen; "abgelehnt" hat einen
eigenen Weg, weil es eine Begründung braucht. */
if (!["angenommen", "arbeit", "erledigt"].includes(ziel)) {
return res.status(400).json({ fehler: "ungueltiger_zustand" });
}
if (z.zustand === "abgelehnt") return res.status(409).json({ fehler: "abgelehnt" });
VanVan im Support, Meldung #8: „Wenn man auf ich fange an drückt
steht dort in Bearbeitung und wenn man auf fertig drückt dann wird
es zu erledigt. DIE AUFGABE BLEIBT ABER IM STATUS OFFEN STEHEN."
/* ==== EINE DAUERHAFTE AUFGABE WIRD NICHT FERTIG (30.09.2026) ====
Gestern habe ich die zwei Knöpfe entfernt, die diese Route riefen,
und daneben geschrieben: „Der Weg bleibt bestehen — er ist die
Schranke, falls ihn jemand direkt anspricht."
VanVan im Support: „… dass die Aufgabe dauerhaft sein soll und
somit nicht vom Modi in den Status erledigt gesetzt werden
kann."
DAS WAR EINE AUSREDE, KEINE BEGRÜNDUNG. Eine Route ist keine
Schranke gegen sich selbst. Sie setzte weiterhin NUR
`aufgaben_zuteilung.zustand` und ließ `aufgaben.status` stehen —
also genau den Zustand, den VanVan gemeldet hat. Unsichtbar
gemacht, nicht abgeschafft.
Sie ist keine Aufgabe, die man abarbeitet, sondern eine, die
man TUT. „Neue begruessen" ist nicht fertig, wenn man es einmal
gemacht hat.
NACHGEMESSEN, BEVOR SIE WEGKAM: Kein einziger Aufruf mehr im
ausgelieferten Browsercode (`grep` über alle JS- und HTML-Dateien
des Workspace). Nur zwei Prüfungen benutzten sie — und eine davon
nickte den Fehler sogar ab: Sie setzte „in Bearbeitung" und prüfte
HTTP 200, nie den Aufgabenstatus daneben.
„ICH FANGE AN" BLEIBT ERLAUBT. Auch eine stehende Aufgabe hat
einen Anfang, und der Unterschied zwischen „angenommen" und „in
Arbeit" sagt etwas. Gesperrt ist nur das Abhaken.
WAS STATTDESSEN GILT: `PATCH /workspace/api/aufgaben/:id` mit
`{ status }`. Er setzt den Status UND zieht die Zuteilung mit
(`zuteilungenNachStatus`), kennt dieselbe Sperre für dauerhafte
Aufgaben und dieselbe Rechteprüfung. Eine Frage, eine Antwort.
DIE SPERRE STEHT HIER UND NICHT NUR IM BROWSER. Ein fehlender
Knopf ist eine Bitte; abgelehnt wird am Server -- wer die
Schnittstelle direkt anspricht, kaeme sonst durch.
ENTFERNT STATT AUSKOMMENTIERT — dieselbe Entscheidung wie bei
`/vorlagen/hilfe` am 01.09.: Eine Route, die niemand mehr aufruft,
wird beim nächsten Mal für lebenden Code gehalten und mitgepflegt.
================================================================== */
BEENDET WIRD SIE VON DER LEITUNG, ueber den Status der Aufgabe
selbst. Eine Pflicht, die niemand mehr beenden kann, waere eine
Falle statt einer Regel. */
if (ziel === "erledigt" && a.dauerhaft) {
return res.status(409).json({ fehler: "dauerhafte_aufgabe" });
}
db().prepare(`UPDATE aufgaben_zuteilung
SET zustand = ?, erledigt_am = ?
WHERE id = ?`).run(ziel, ziel === "erledigt" ? jetzt() : null, z.id);
protokolliere("aufgabe_stand", {
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
detail: `#${a.id} ${ziel}`.slice(0, 120),
});
res.json({ ok: true, zustand: ziel });
} catch (f) {
console.error("[zuteilung] mein-stand:", f?.message);
res.status(503).json({ fehler: "nicht_verfuegbar" });
}
});
/* ---- Bewerten (Abschnitt 5) ----------------------------------------- */
zuteilungRouter.post("/workspace/api/aufgaben/:id/bewerten", (req, res) => {