Files
skills/orchestrator/templates/style-tasks.css
T
Kevin Veen-BirkenbachandClaude Opus 5.5 d9eb121d12 feat(orchestrator): S5 Tasks with choices, Gantt, estimates and burn charts
Requirement 001 phase 6, S5:

- Tasks tab: every pending ask, rule request, operator row and choice
  card shows under "waits on Operator"; a choice request goes out on a
  fixed channel button, post and call with an agent multi-select, and a
  click on an option sends a passkey-signed answer the agent checks with
  operator_message. The unsigned /api/decisions/<id>/choose route is
  gone, and a decision, choice answer or choice recipient whose name
  holds "*" is refused, so a signed answer never reaches every agent.
- Gantt view: one bar per task from start to end (running to now), one
  arrow per explicit, branch-base and queue-order edge, hatched forecast
  bars (always, points only, real only) from the median duration or
  points times median hours per point.
- rasci.py set --after/--drop-after stores task dependencies and refuses
  unknown tasks and cycles.
- Estimation toggle (No estimate default, Story points) is stored in the
  hub settings; burndown and burnup per whole register, list filter or
  one agent or line, counting tasks or summing complexity with the
  unestimated tasks as their own line.
- New skill kvb-org-queue-estimate: "Request estimates" (story points
  mode, passkey-signed board action) or the skill sends exactly one
  newsletter listing every unestimated task with its RASCI parties; a
  party name that is no plain name shows as "unnamed party".
- Business value per task or in bulk is stored only with a verified
  passkey assertion over the exact ids and values; WSJF uses it instead
  of the median. The task list sorts by every decision-50 mode both ways.
- e2e: the walk's pending ask lives an hour instead of 60 s, and the
  CDP and BiDi drivers retry removing the browser profile while the
  browser exits (ENOTEMPTY).

Evidence: make test 709 OK, ruff and markdownlint clean
(/tmp/s5-make-test-fix-2.log); e2e 26 OK in Chromium and Firefox
(/tmp/s5-e2e-fix-2.log); MEDUSA 50 files, an independent skeptic
rejected four holes (unsigned choose route, unsigned estimates request,
free-text party names, "*" recipients incl. normalizer bypasses), all
fixed and re-verified, the 8 remaining pattern hits accepted, rescan 0
open.

🎯 Fix: 92 %
💥 Break: 4 %
💸 Cost: 100 %
⏱️ Time: –
🛡️ Security: 100 %
🏆 Score: 88
🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 %

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 06:53:55 +00:00

80 lines
6.3 KiB
CSS

.graph.tasks { grid-template-columns: minmax(0, 1fr); }
.tasks-body { min-width: 0; }
.tasks .controls input[type="search"] { flex: 1; min-width: min(240px, 100%); }
.tasks > .chips { margin-bottom: 0; }
body.fit .graph.tasks > .tasks-body, body.fit .graph.tasks > .inbox { flex: 1; min-height: 0; overflow: auto; }
.choice-request { gap: var(--sp-2) var(--sp-3); }
.choice-agents { display: grid; gap: 6px; max-height: 40vh; overflow: auto; }
.chip .count { margin-left: 2px; }
.task-list, .task-cards { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.task-row { display: grid; gap: 6px; min-width: 0; padding: var(--sp-3); border: 1px solid var(--line); border-radius: 12px; background: var(--glass-card); }
.task-row .head-row { margin-bottom: 0; }
.task-row > .linkish { justify-self: start; font-size: var(--fs-lg); text-align: left; }
.task-row.open-work, .task-card.open-work, .wt.open-work, .matrix tr.open-work > th { box-shadow: inset 3px 0 0 var(--warn); }
.task-row.ended, .task-card.ended { opacity: .72; }
.task-id { font-size: var(--fs-sm); color: var(--muted); overflow-wrap: anywhere; }
.task-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; min-width: 0; color: var(--muted); font-size: var(--fs-sm); }
.task-meta code { overflow-wrap: anywhere; }
.task-board { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)); gap: 12px; align-items: start; }
.task-column { display: grid; gap: 8px; min-width: 0; padding: var(--sp-3); }
.task-card { display: grid; gap: 4px; min-width: 0; padding: 8px 10px; border: 1px solid var(--line); border-radius: 10px; background: var(--sunken); font-size: var(--fs-sm); }
.task-card .linkish { overflow-wrap: anywhere; }
.flow { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; min-width: 0; font-size: var(--fs-sm); }
.flow-label { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); font-weight: 650; }
.flow-side { display: inline-flex; flex-wrap: wrap; gap: 4px; min-width: 0; }
.flow-ref { padding: 0 6px; border: 1px solid var(--line); border-radius: 6px; background: var(--sunken); line-height: 20px; overflow-wrap: anywhere; }
.flow-ref.here { border-color: transparent; background: var(--accent-soft); color: var(--accent); font-weight: 650; }
.flow-arrow { display: inline-flex; color: var(--muted); }
.flow.invalid { gap: 6px; color: var(--bad); }
.states.work .state { gap: 5px; }
.matrix-box { display: grid; gap: 8px; min-width: 0; }
.matrix-scroll { max-width: 100%; max-height: 70vh; overflow: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--glass-card); }
table.matrix { border-collapse: separate; border-spacing: 0; font-size: var(--fs-sm); }
table.matrix th, table.matrix td { padding: 6px 8px; border-bottom: 1px solid var(--line); text-align: center; vertical-align: middle; white-space: nowrap; }
table.matrix thead th { position: sticky; top: 0; z-index: 1; background: var(--panel); vertical-align: bottom; }
table.matrix th[scope="row"], table.matrix thead th:first-child { position: sticky; left: 0; background: var(--panel); text-align: left; }
table.matrix thead th:first-child { z-index: 2; }
table.matrix th[scope="row"] { z-index: 1; min-width: min(240px, 52vw); max-width: 320px; white-space: normal; }
.matrix-task { display: grid; gap: 2px; }
.matrix .party { display: inline-block; max-height: 150px; overflow: hidden; text-overflow: ellipsis; writing-mode: vertical-rl; transform: rotate(180deg); }
.matrix td .letter + .letter { margin-left: 2px; }
.task-edit { display: grid; gap: 8px; }
.task-edit h3 { margin: 6px 0 0; color: var(--muted); font-size: var(--fs-xs); font-weight: 650; letter-spacing: .04em; text-transform: uppercase; }
.task-form { display: flex; flex-wrap: wrap; align-items: end; gap: 10px; }
.task-form label { display: grid; gap: 4px; color: var(--muted); font-size: var(--fs-sm); }
.task-form input[type="number"] { width: 120px; }
.plan-bar { gap: var(--sp-2) var(--sp-3); }
.plan-bar [hidden] { display: none; }
.plan-group { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; color: var(--muted); font-size: var(--fs-sm); }
.plan-group select.field { width: auto; min-width: 0; max-width: min(260px, 100%); }
.task-pick { margin: 0; accent-color: var(--accent); }
.gantt-box, .burn-box { display: grid; gap: 8px; min-width: 0; }
.gantt-scroll { max-width: 100%; overflow: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--glass-card); }
.gantt-scroll svg.gantt { display: block; max-width: none; }
svg .gantt-bar, svg .gantt-dot { fill: color-mix(in srgb, var(--c, var(--accent)) 70%, var(--panel)); stroke: var(--c, var(--accent)); stroke-width: 1; }
svg .gantt-bar.forecast, svg .gantt-dot.forecast { fill: url(#gantt-hatch); stroke-dasharray: 3 2; }
svg .tone-ok { --c: var(--ok); }
svg .tone-bad { --c: var(--bad); }
svg .tone-info { --c: var(--info); }
svg .tone-warn { --c: var(--warn); }
svg .tone-neutral { --c: var(--muted); }
svg .hatch-ground { fill: var(--accent-soft); }
svg .hatch-line { stroke: var(--accent); stroke-width: 3; }
svg .gantt-edge { fill: none; stroke: var(--c); stroke-width: 1.6; opacity: .8; }
svg .gantt-edge.k-after, .gantt-key.k-after { --c: var(--violet); }
svg .gantt-edge.k-base, .gantt-key.k-base { --c: var(--info); }
svg .gantt-edge.k-queue, .gantt-key.k-queue { --c: var(--warn); }
svg .gantt-tip { fill: var(--muted); }
.gantt-key, .burn-key { display: inline-flex; align-items: center; gap: 6px; }
.gantt-key::before, .burn-key::before { content: ""; width: 18px; height: 4px; border-radius: 2px; background: var(--c, var(--accent)); }
.gantt-key.forecast::before { height: 10px; background: repeating-linear-gradient(45deg, var(--accent) 0 3px, var(--accent-soft) 3px 6px); }
svg.burn { width: 100%; height: auto; max-height: 60vh; }
svg .burn-line { fill: none; stroke: var(--c); stroke-width: 2.4; }
svg .burn-line.k-scope, .burn-key.k-scope { --c: var(--info); }
svg .burn-line.k-done, .burn-key.k-done { --c: var(--ok); }
svg .burn-line.k-remaining, .burn-key.k-remaining { --c: var(--accent); }
svg .burn-line.k-unestimated, .burn-key.k-unestimated { --c: var(--warn); }
svg .burn-line.k-unestimated { stroke-dasharray: 6 4; }
svg .node.a-worktree.open circle { stroke: var(--warn); stroke-width: 2.6; stroke-dasharray: 3 2; }
svg .edge.flow { stroke-dasharray: 2 5; }