Qualify wait states in the coordination graph (CUST-WP-0074-T04)
Task nodes carry wait_kind (external | human | both | unqualified, null unless wait): the hub field wins, otherwise derived from needs_human and dependency edges (task-level depends_on, or a workplan dependency row whose target the task cites). Workplan nodes carry blocked_kind (human | external | none, null unless blocked). Task-level depends_on becomes a task -> workplan/task depends_on edge; depends_on edges carry edge_kind commitment (cyan #0891b2), human gates are marked on the node (humanGate, rose #be123c). A needs_human=true query / "Needs human" toggle / --needs-human flag keeps only blocked_kind=human workplans and their human-wait tasks; the parameter survives in the shareable URL and mode switch like include_residuals. Fabric stays a read of State Hub. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 237582@bnt-lap001 Assistant-Session: f2b3d9f1-8fb9-4b9c-bc2b-837ec5dfc826
This commit is contained in:
parent
b47dd43b04
commit
9dbb8f0bcb
7 changed files with 426 additions and 33 deletions
|
|
@ -409,6 +409,10 @@ def graph_explorer_page() -> str:
|
|||
<span class="field-label">Residuals <button type="button" class="help-tip" aria-label="Residuals help" data-help-title="Include residuals" data-help="Default coordination view omits flavor=residual workplans. Enable this to show them. Unspecified flavor stays visible. Titles containing the word residual are not treated as residual.">?</button></span>
|
||||
<input type="checkbox" id="include-residuals" aria-label="Include residual workplans">
|
||||
</label>
|
||||
<label id="needs-human-wrap" class="field" style="display:none;align-self:end">
|
||||
<span class="field-label">Needs human <button type="button" class="help-tip" aria-label="Needs human help" data-help-title="Needs human only" data-help="Show only workplans blocked on a human gate (blocked_kind=human) and their human-wait tasks. External commitments (depends_on) are hidden. This is the operator queue.">?</button></span>
|
||||
<input type="checkbox" id="needs-human" aria-label="Needs human only">
|
||||
</label>
|
||||
<div class="field">
|
||||
<span class="field-label">Layout <button type="button" class="help-tip" aria-label="Layout help" data-help-title="Layout" data-help="Layout redraws the map arrangement. Cose uses relationship strength and repo affinity; circle, grid, concentric, and breadthfirst are simpler alternate arrangements.">?</button></span>
|
||||
<select id="layout-select" aria-label="Graph layout">
|
||||
|
|
@ -572,8 +576,13 @@ def graph_explorer_page() -> str:
|
|||
let includeResidualsParam = ["1", "true", "yes"].includes(
|
||||
(pageParams.get("include_residuals") || "").toLowerCase()
|
||||
);
|
||||
let needsHumanParam = ["1", "true", "yes"].includes(
|
||||
(pageParams.get("needs_human") || "").toLowerCase()
|
||||
);
|
||||
const coordinationUrl = (includeResiduals, needsHuman) =>
|
||||
`/exports/graph-explorer?mode=coordination${includeResiduals ? "&include_residuals=true" : ""}${needsHuman ? "&needs_human=true" : ""}`;
|
||||
const graphUrl = graphMode === "coordination"
|
||||
? `/exports/graph-explorer?mode=coordination${includeResidualsParam ? "&include_residuals=true" : ""}`
|
||||
? coordinationUrl(includeResidualsParam, needsHumanParam)
|
||||
: "/exports/graph-explorer";
|
||||
const canvas = document.getElementById("graph-canvas");
|
||||
const zoneOverlay = document.getElementById("zone-overlay");
|
||||
|
|
@ -2142,6 +2151,7 @@ def graph_explorer_page() -> str:
|
|||
if (state.search) params.set("search", state.search);
|
||||
if (state.mode && state.mode !== "full") params.set("mode", state.mode);
|
||||
if (state.mode === "coordination" && includeResidualsParam) params.set("include_residuals", "true");
|
||||
if (state.mode === "coordination" && needsHumanParam) params.set("needs_human", "true");
|
||||
if (state.layout && state.layout !== "cose") params.set("layout", state.layout);
|
||||
if (state.labelMode && state.labelMode !== "auto") params.set("labelMode", state.labelMode);
|
||||
if (state.nodeTypes.length !== allNodeTypes.length) params.set("nodeTypes", state.nodeTypes.join(","));
|
||||
|
|
@ -2203,7 +2213,7 @@ def graph_explorer_page() -> str:
|
|||
|
||||
const applyViewState = (state, options = {}) => {
|
||||
if (cy && "mode" in state && ((state.mode === "coordination") !== (graphMode === "coordination"))) {
|
||||
switchGraph(state.mode, includeResidualsParam, state);
|
||||
switchGraph(state.mode, includeResidualsParam, state, needsHumanParam);
|
||||
return;
|
||||
}
|
||||
if ("search" in state) searchInput.value = state.search || "";
|
||||
|
|
@ -2836,6 +2846,15 @@ def graph_explorer_page() -> str:
|
|||
switchGraph("coordination", residualsToggle.checked);
|
||||
});
|
||||
}
|
||||
const needsHumanWrap = document.getElementById("needs-human-wrap");
|
||||
const needsHumanToggle = document.getElementById("needs-human");
|
||||
if (needsHumanWrap && needsHumanToggle) {
|
||||
needsHumanWrap.style.display = graphMode === "coordination" ? "" : "none";
|
||||
needsHumanToggle.checked = needsHumanParam;
|
||||
needsHumanToggle.addEventListener("change", () => {
|
||||
switchGraph("coordination", includeResidualsParam, null, needsHumanToggle.checked);
|
||||
});
|
||||
}
|
||||
profiles = loadProfiles();
|
||||
renderProfiles();
|
||||
const elements = (payload.elements || []).map((element) => ({
|
||||
|
|
@ -2903,6 +2922,10 @@ def graph_explorer_page() -> str:
|
|||
{selector: "node[flavor = 'refactoring']", style: {"background-color": "#b45309"}},
|
||||
{selector: "node[flavor = 'extension']", style: {"background-color": "#0f766e"}},
|
||||
{selector: "node[flavor = 'residual']", style: {"background-color": "#64748b"}},
|
||||
{selector: "edge[edge_kind = 'commitment']", style: {"line-color": "#0891b2", "target-arrow-color": "#0891b2"}},
|
||||
{selector: "node[wait_kind = 'external']", style: {"background-color": "#0891b2"}},
|
||||
{selector: "node[humanGate = true]", style: {"background-color": "#be123c", "border-color": "#be123c", "border-style": "solid", "border-width": 3}},
|
||||
{selector: "node[wait_kind = 'both']", style: {"border-color": "#0891b2"}},
|
||||
{selector: "edge[zoneCollapse = true]", style: {"line-style": "dashed", "line-color": "#0f766e", "target-arrow-color": "#0f766e"}},
|
||||
{selector: "node.rule-highlight", style: {
|
||||
"border-color": "#2563eb",
|
||||
|
|
@ -2974,11 +2997,11 @@ def graph_explorer_page() -> str:
|
|||
};
|
||||
|
||||
let graphRequest = 0;
|
||||
const switchGraph = async (mode, includeResiduals = includeResidualsParam, viewState = null) => {
|
||||
const switchGraph = async (mode, includeResiduals = includeResidualsParam, viewState = null, needsHuman = needsHumanParam) => {
|
||||
const request = ++graphRequest;
|
||||
const coordination = mode === "coordination";
|
||||
const url = coordination
|
||||
? `/exports/graph-explorer?mode=coordination${includeResiduals ? "&include_residuals=true" : ""}`
|
||||
? coordinationUrl(includeResiduals, needsHuman)
|
||||
: "/exports/graph-explorer";
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
|
|
@ -2992,10 +3015,13 @@ def graph_explorer_page() -> str:
|
|||
}));
|
||||
graphMode = coordination ? "coordination" : "full";
|
||||
includeResidualsParam = includeResiduals;
|
||||
needsHumanParam = needsHuman;
|
||||
activeMode = mode;
|
||||
modeSelect.value = mode;
|
||||
document.getElementById("include-residuals-wrap").style.display = coordination ? "" : "none";
|
||||
document.getElementById("include-residuals").checked = includeResiduals;
|
||||
document.getElementById("needs-human-wrap").style.display = coordination ? "" : "none";
|
||||
document.getElementById("needs-human").checked = needsHuman;
|
||||
selected = null;
|
||||
selectedAnchor = null;
|
||||
selectedZoneId = "";
|
||||
|
|
@ -3031,6 +3057,7 @@ def graph_explorer_page() -> str:
|
|||
if (request !== graphRequest) return;
|
||||
modeSelect.value = activeMode;
|
||||
document.getElementById("include-residuals").checked = includeResidualsParam;
|
||||
document.getElementById("needs-human").checked = needsHumanParam;
|
||||
profileSummary.textContent = `Could not load graph: ${error.message}`;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue