@quolu/lattice 0.12.14 → 0.12.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quolu/lattice",
3
- "version": "0.12.14",
3
+ "version": "0.12.15",
4
4
  "description": "Lattice — phase-aware TODO graph compiler and conflict-aware orchestration runtime",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -6,7 +6,7 @@ import {
6
6
  } from './todo-markdown-renderer.mjs';
7
7
  import { renderTodoGanttSvg, TODO_GANTT_STATUS_PRESENTATION } from './todo-gantt-svg.mjs';
8
8
 
9
- export const TODO_GANTT_RENDERER_VERSION = 'lattice.todo_gantt_renderer.v11';
9
+ export const TODO_GANTT_RENDERER_VERSION = 'lattice.todo_gantt_renderer.v12';
10
10
  export const TODO_GANTT_PROSE_MAX_BYTES = 8 * 1024 * 1024;
11
11
  export const TODO_GANTT_HTML_MAX_BYTES = 24 * 1024 * 1024;
12
12
 
@@ -190,8 +190,12 @@ function renderRelationList(relations, sectionByKey, lookup, emptyText, folds =
190
190
  }).join('')}</ul>`;
191
191
  }
192
192
 
193
+ /** Phase states that are over: nothing is dispatched or judged under them again. */
194
+ const SETTLED_PHASE_STATUS = Object.freeze(['accepted', 'rejected']);
195
+
193
196
  function renderPhaseProgress(readModel) {
194
197
  const rows = [];
198
+ const settledRows = [];
195
199
  for (const member of readModel.members) {
196
200
  if (!['lattice.todo_plan.v4', 'lattice.todo_plan.v5'].includes(member.plan.schema)) continue;
197
201
  const phases = new Map(member.snapshot.phases.map((phase) => [phase.phase_id, phase]));
@@ -200,15 +204,23 @@ function renderPhaseProgress(readModel) {
200
204
  const states = new Map(member.tasks.map((task) => [task.task_id, task.status]));
201
205
  const done = tasks.filter((task) => states.get(task.task_id) === 'done').length;
202
206
  const state = phases.get(phase.phase_id);
203
- rows.push(`<li class="phase-progress status-${escapeHtmlAttribute(state.status)}"><header><strong>${escapeHtmlText(phase.title ?? phase.phase_id)}</strong><span>${escapeHtmlText(state.status)}</span></header><p><code>${escapeHtmlText(`${member.plan.plan_key}/${phase.phase_id}`)}</code> — policy <code>${escapeHtmlText(phase.gate_policy)}</code> — ToDo ${done}/${tasks.length}</p><progress max="${tasks.length}" value="${done}">${done}/${tasks.length}</progress></li>`);
207
+ const row = `<li class="phase-progress status-${escapeHtmlAttribute(state.status)}"><header><strong>${escapeHtmlText(phase.title ?? phase.phase_id)}</strong><span>${escapeHtmlText(state.status)}</span></header><p><code>${escapeHtmlText(`${member.plan.plan_key}/${phase.phase_id}`)}</code> — policy <code>${escapeHtmlText(phase.gate_policy)}</code> — ToDo ${done}/${tasks.length}</p><progress max="${tasks.length}" value="${done}">${done}/${tasks.length}</progress></li>`;
208
+ // A settled Phase is history. It stays reachable, but it does not push the
209
+ // live ones off the first screen.
210
+ (SETTLED_PHASE_STATUS.includes(state.status) ? settledRows : rows).push(row);
204
211
  }
205
212
  }
206
213
  const decoupled = readModel.members.some(({ plan }) => plan.schema === 'lattice.todo_plan.v5');
207
214
  const guidance = decoupled
208
215
  ? 'ToDo完了とPhase受理は別です。Phaseは重監査の順序を表し、通常ToDoの開始順はToDo依存だけで決まります。'
209
216
  : 'ToDo完了とPhase受理は別です。<code>gate_ready</code>では後続Phaseはまだ解放されません。';
210
- return rows.length === 0 ? ''
211
- : `<section class="phase-overview"><h2>Phase進捗</h2><p>${guidance}</p><ol>${rows.join('')}</ol></section>`;
217
+ if (rows.length === 0 && settledRows.length === 0) return '';
218
+ const liveList = rows.length === 0
219
+ ? '<p class="readiness-note">進行中のPhaseはありません。</p>'
220
+ : `<ol>${rows.join('')}</ol>`;
221
+ const settledList = settledRows.length === 0 ? ''
222
+ : `<details class="phase-settled"><summary>決着済みPhase ${settledRows.length}件</summary><ol>${settledRows.join('')}</ol></details>`;
223
+ return `<section class="phase-overview"><h2>Phase進捗</h2><p>${guidance}</p>${liveList}${settledList}</section>`;
212
224
  }
213
225
 
214
226
  function renderRightPane(sections, layout, presentation, readModel) {
@@ -347,7 +359,8 @@ body{display:grid;grid-template-rows:minmax(0,1fr);height:100vh;margin:0;backgro
347
359
  .fold-chip{padding:2px 8px;border:1px solid var(--border);border-radius:9999px;background:var(--surface-2);color:var(--text-primary);font-weight:650}
348
360
  .fold-note{flex:1 0 100%;margin:4px 0 0;color:var(--text-secondary);font-weight:400}
349
361
  .task-index-folded{margin-top:8px}
350
- .task-index-folded>summary{cursor:pointer;padding:6px 0;color:var(--text-secondary);font-weight:600}
362
+ .task-index-folded>summary,.phase-settled>summary{cursor:pointer;padding:6px 0;color:var(--text-secondary);font-weight:600}
363
+ .phase-settled>summary:focus-visible{outline:2px solid var(--text-primary);outline-offset:2px}
351
364
  .status-in-progress .node-surface{fill:var(--surface-1);stroke:var(--accent);stroke-width:2}
352
365
  .status-in-progress .status-mark{fill:var(--accent)}
353
366
  .status-in-progress .status-bar{stroke:var(--accent);stroke-width:2;stroke-linecap:round}