Verlorene Updates erkennen statt warten: optimistische Pruefung beim Ersetzen, Konflikte im Audit und im Lagebild (#720)
FileLedger merkt je Agent die gelesene Fassung einer Datei in gemeinsamen Bereichen. write, copy und Downloads (FTP, RocketChat) ersetzen eine Datei nur, wenn sie seit dem Lesen unveraendert ist; eine fremde Datei nicht ungelesen. append bleibt ungeprueft, macht aeltere Lesestaende aber ungueltig. Abgelehnte Schreibvorgaenge: eigener Audit-Status Conflict, Tabelle FileConflicts, Hotspots (>= 3 in 7 Tagen) in der Teamuebersicht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8a7212c240
commit
79a3a7854f
@@ -869,6 +869,32 @@ button.klein {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* #720: files with repeated write conflicts, above the team canvas */
|
||||
.team-konflikte {
|
||||
border: 1px solid var(--divider);
|
||||
border-left: 4px solid var(--gefahr, #c0392b);
|
||||
background: var(--surface);
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.team-konflikte[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.team-konflikte p {
|
||||
margin: 4px 0 6px;
|
||||
}
|
||||
|
||||
.team-konflikte ul {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
.team-konflikte code {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* #712: the runtime card */
|
||||
.laufzeit-feld {
|
||||
display: grid;
|
||||
|
||||
@@ -982,5 +982,9 @@
|
||||
"runtime.oauthToken": "Abo-Token",
|
||||
"runtime.oauthTokenHint": "Erzeugt mit 'claude setup-token' auf einem Rechner, an dem du angemeldet bist.",
|
||||
"runtime.keepStored": "hinterlegt – leer lassen zum Behalten",
|
||||
"runtime.accountSaved": "Claude-Code-Konto gespeichert."
|
||||
"runtime.accountSaved": "Claude-Code-Konto gespeichert.",
|
||||
|
||||
"team.conflicts.title": "Dateien, in denen sich Agenten gegenseitig überschreiben",
|
||||
"team.conflicts.hint": "Hier wurden Schreibvorgänge abgelehnt, weil sie die Änderung eines anderen Agenten verloren hätten. Passiert das oft, sollte die Ablage anders werden: eine Datei je Agent oder Tag, Anhängen statt Ersetzen oder eine Tabelle.",
|
||||
"team.conflicts.count": "{n} Konflikte in 7 Tagen, zwischen {agents}; zuletzt {time}"
|
||||
}
|
||||
|
||||
@@ -982,5 +982,9 @@
|
||||
"runtime.oauthToken": "Subscription token",
|
||||
"runtime.oauthTokenHint": "Created with 'claude setup-token' on a machine where you are signed in.",
|
||||
"runtime.keepStored": "stored — leave empty to keep",
|
||||
"runtime.accountSaved": "Claude Code account saved."
|
||||
"runtime.accountSaved": "Claude Code account saved.",
|
||||
|
||||
"team.conflicts.title": "Files agents keep overwriting each other in",
|
||||
"team.conflicts.hint": "Writes were refused here because they would have lost another agent's change. If it keeps happening, change how the file is kept: one file per agent or day, appending instead of replacing, or a table.",
|
||||
"team.conflicts.count": "{n} conflicts in 7 days, between {agents}; last {time}"
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ let matchInfo = null;
|
||||
let zoomValue = null;
|
||||
let panel = null;
|
||||
let summary = null;
|
||||
let hotspots = null;
|
||||
|
||||
let unsubscribe = null;
|
||||
let refreshTimer = null;
|
||||
@@ -151,8 +152,12 @@ export async function renderTeam() {
|
||||
panel = erstelleElement("aside", { klasse: "team-seitenleiste" });
|
||||
panel.hidden = true;
|
||||
|
||||
// #720: files agents keep overwriting each other in — hidden while there are none.
|
||||
hotspots = erstelleElement("div", { klasse: "team-konflikte", role: "status" });
|
||||
hotspots.hidden = true;
|
||||
|
||||
const area = erstelleElement("div", { klasse: "team-arbeitsflaeche" }, buildTree(), canvas, panel);
|
||||
container.replaceChildren(erstelleElement("div", { klasse: "team-ansicht" }, head, area, buildLegend()));
|
||||
container.replaceChildren(erstelleElement("div", { klasse: "team-ansicht" }, head, hotspots, area, buildLegend()));
|
||||
|
||||
if (!holeAktiveInstanz()) {
|
||||
canvas.replaceChildren(erstelleElement("div", { klasse: "hinweis-zentriert", text: t("team.noInstance") }));
|
||||
@@ -1068,6 +1073,28 @@ function renderSummary() {
|
||||
summary.replaceChildren(
|
||||
...(parts.length ? parts : [erstelleElement("span", { klasse: "gedaempft", text: t("team.empty") })])
|
||||
);
|
||||
renderHotspots();
|
||||
}
|
||||
|
||||
/**
|
||||
* #720: Files with repeated write conflicts in the last week. Detecting is code; what to do
|
||||
* about it — split the file, append instead, make it a table — is a human's call.
|
||||
*/
|
||||
function renderHotspots() {
|
||||
if (!hotspots || !snapshot) return;
|
||||
const list = snapshot.conflictHotspots ?? [];
|
||||
hotspots.hidden = list.length === 0;
|
||||
if (list.length === 0) {
|
||||
hotspots.replaceChildren();
|
||||
return;
|
||||
}
|
||||
hotspots.replaceChildren(
|
||||
erstelleElement("strong", { text: `⚠ ${t("team.conflicts.title")}` }),
|
||||
erstelleElement("p", { klasse: "gedaempft text-klein", text: t("team.conflicts.hint") }),
|
||||
erstelleElement("ul", {},
|
||||
...list.map((h) => erstelleElement("li", {},
|
||||
erstelleElement("code", { text: h.path }),
|
||||
` — ${t("team.conflicts.count", { n: h.conflicts, agents: h.agents.join(", "), time: formatiereDatumZeit(h.lastAtUtc) })}`))));
|
||||
}
|
||||
|
||||
/** The card tones by the names the badges use. */
|
||||
|
||||
Reference in New Issue
Block a user