Besprechung als eigenes Mittel: Termin, Tagesordnung, eine Runde je Teilnehmer, Protokoll (#686)

Aufgabentyp meeting mit Teilnehmerliste (Frontmatter, SQLite, Taskboard-Werkzeug,
WebUI). MeetingRunner: jeder Teilnehmer einmal in Reihenfolge, Beitraege
gerahmt als Daten des Verfassers, danach schliesst die Leitung mit Ergebnis und
Aufgaben. Protokoll schreibt die Instanz in den Bereich der Einheit der Leitung.
Teilnehmer nur aus dem Adressbuch der Leitung, hoechstens 12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-28 13:12:13 +02:00
co-authored by Claude Opus 5.5
parent 2df2fc476f
commit f2bf8b5b59
19 changed files with 684 additions and 25 deletions
@@ -1030,5 +1030,10 @@
"fileHistory.reasonPlaceholder": "Warum wird diese Fassung zurückgeholt?",
"fileHistory.reasonMissing": "Eine Wiederherstellung braucht einen Grund.",
"fileHistory.restored": "{path} wiederhergestellt.",
"fileHistory.failed": "Fehlgeschlagen: {error}"
"fileHistory.failed": "Fehlgeschlagen: {error}",
"tasks.participants": "Besprechungsteilnehmer",
"tasks.participantsPlaceholder": "agent-a, agent-b — leer: keine Besprechung",
"tasks.participantsHint": "Mit Teilnehmern ist die Aufgabe eine Besprechung: Jeder spricht einmal, in dieser Reihenfolge, mit Tagesordnung (der Aufgabentext) und dem bisher Gesagten; der zugewiesene Agent leitet, schreibt das Ergebnis und legt die Aufgaben dafür an. Das Protokoll landet im Bereich seiner Einheit.",
"tasks.meetingWith": "Besprechung mit {participants}"
}
@@ -1030,5 +1030,10 @@
"fileHistory.reasonPlaceholder": "Why is this version put back?",
"fileHistory.reasonMissing": "A restore needs a reason.",
"fileHistory.restored": "{path} restored.",
"fileHistory.failed": "Failed: {error}"
"fileHistory.failed": "Failed: {error}",
"tasks.participants": "Meeting participants",
"tasks.participantsPlaceholder": "agent-a, agent-b — empty: not a meeting",
"tasks.participantsHint": "With participants the task is a meeting: each speaks once, in this order, with the agenda (the task message) and what was said before; the assigned agent chairs, writes the result and creates the tasks for it. Minutes go to the chair's unit workspace.",
"tasks.meetingWith": "Meeting with {participants}"
}
@@ -275,6 +275,13 @@ function erstelleAufgabenKarte(task) {
text: t("tasks.estimate", { usd: betrag(task.estimateUsd) })
}));
}
// #686: a meeting — who takes part, in the order they speak.
if (task.participants?.length) {
zeilen.push(erstelleElement("p", {
klasse: "text-klein",
text: `◎ ${t("tasks.meetingWith", { participants: task.participants.join(", ") })}`
}));
}
// #716: What the budget refused, and what happens next.
const ausfall = task.budgetMiss;
@@ -414,6 +421,14 @@ async function oeffneAufgabenDialog(task) {
value: task?.estimateUsd != null ? String(task.estimateUsd) : ""
});
// #686: participants make the task a meeting, chaired by the assignee.
const teilnehmer = erstelleElement("input", {
type: "text",
value: (task?.participants || []).join(", "),
placeholder: t("tasks.participantsPlaceholder"),
title: t("tasks.participantsHint")
});
const hinweis = erstelleElement("p", { klasse: "gedaempft text-klein" });
const aktualisiere = () => {
@@ -446,6 +461,7 @@ async function oeffneAufgabenDialog(task) {
erstelleElement("label", { text: t("tasks.parent") + ":" }), eltern,
erstelleElement("label", { text: t("tasks.fillBy") + ":", title: t("tasks.fillByHint") }), fuellBis,
erstelleElement("label", { text: t("tasks.estimateUsd") + ":" }), schaetzung,
erstelleElement("label", { text: t("tasks.participants") + ":", title: t("tasks.participantsHint") }), teilnehmer,
erstelleElement("span"), hinweis
);
@@ -479,7 +495,8 @@ async function oeffneAufgabenDialog(task) {
priority: task?.priority ?? 3,
parentId: eltern.value.trim(),
fillBy: fuellBis.value || null,
estimateUsd: schaetzung.value === "" ? null : Number(schaetzung.value)
estimateUsd: schaetzung.value === "" ? null : Number(schaetzung.value),
participants: teilnehmer.value.split(",").map((x) => x.trim()).filter((x) => x.length > 0)
};
try {