Skip to content

Commit 0569516

Browse files
committed
feat: productize planner desk and wake scheduling
1 parent 68f94c9 commit 0569516

18 files changed

Lines changed: 735 additions & 86 deletions

File tree

apps/dashboard/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ repository is already a finished consumer product.
4242

4343
- PM workspace: registry-driven task-pack selection plus `execution_plan_report`
4444
preview before execution starts.
45+
- Planner desk: a first-class planner-facing route that pulls `planning_wave_plan`,
46+
`planning_worker_prompt_contracts`, `planning_unblock_tasks`, and
47+
`completion_governance_report` back into one read-only planning surface
48+
instead of leaving them trapped inside PM sidebars and run detail.
4549
- Agents: the first-screen role catalog now also hosts a repo-owned role
4650
configuration desk for previewing and saving future compiled defaults
4751
(`system_prompt_ref`, bundle refs, and role-level runtime binding) while

apps/dashboard/app/agents/page.tsx

Lines changed: 78 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -419,78 +419,6 @@ export default async function AgentsPage({ searchParams }: AgentsPageProps) {
419419
<p className="cell-sub mono muted">{agentsPageCopy.metricSublines.schedulerHint(lockedAgentCount)}</p>
420420
</article>
421421
</section>
422-
<RoleConfigControlPlane roleCatalog={roleCatalogAll} />
423-
<section className="app-section" aria-labelledby="agents-role-catalog-title">
424-
<div className="section-header">
425-
<div>
426-
<h2 className="sr-only">Role catalog (read-only first screen)</h2>
427-
<h2 id="agents-role-catalog-title" className="section-title">{agentsPageCopy.roleCatalog.title}</h2>
428-
<p className="mono muted">{agentsPageCopy.roleCatalog.subtitle}</p>
429-
</div>
430-
<div className="toolbar" role="group" aria-label="Role catalog entry">
431-
<Button asChild variant="secondary" aria-label="Go to the full registered agent list">
432-
<Link href="#agents-registered-title">{agentsPageCopy.roleCatalog.fullList}</Link>
433-
</Button>
434-
</div>
435-
</div>
436-
<Card variant="table">
437-
{payloadWarning ? (
438-
<p className="mono muted">{agentsPageCopy.roleCatalog.registryUnavailable}</p>
439-
) : roleCatalog.length === 0 ? (
440-
<p className="mono muted">{agentsPageCopy.roleCatalog.noMatches}</p>
441-
) : (
442-
<table className="run-table">
443-
<thead>
444-
<tr>
445-
<th scope="col">{agentsPageCopy.roleCatalog.headers.role}</th>
446-
<th scope="col">{agentsPageCopy.roleCatalog.headers.skillsBundle}</th>
447-
<th scope="col">{agentsPageCopy.roleCatalog.headers.mcpBundle}</th>
448-
<th scope="col">{agentsPageCopy.roleCatalog.headers.runtimeBinding}</th>
449-
<th scope="col">{agentsPageCopy.roleCatalog.headers.executionAuthority}</th>
450-
<th scope="col">{agentsPageCopy.roleCatalog.headers.registeredSeats}</th>
451-
</tr>
452-
</thead>
453-
<tbody>
454-
{roleCatalog.map((roleEntry) => (
455-
<tr key={`role-catalog:${roleEntry.role}`}>
456-
<th scope="row">
457-
<div className="stack-gap-2">
458-
<Badge>{roleEntry.role}</Badge>
459-
<span className="muted">{roleEntry.purpose || agentsPageCopy.roleCatalog.noRolePurpose}</span>
460-
</div>
461-
</th>
462-
<td>
463-
<span className="mono muted">
464-
{formatBindingReadModelLabel(roleEntry.role_binding_read_model?.skills_bundle_ref)}
465-
</span>
466-
</td>
467-
<td>
468-
<span className="mono muted">
469-
{formatBindingReadModelLabel(roleEntry.role_binding_read_model?.mcp_bundle_ref)}
470-
</span>
471-
</td>
472-
<td><span className="mono muted">{formatRoleBindingRuntimeSummary(roleEntry.role_binding_read_model)}</span></td>
473-
<td>
474-
<div className="stack-gap-2">
475-
<Badge variant="running">{roleEntry.role_binding_read_model.execution_authority}</Badge>
476-
<span className="muted">{agentsPageCopy.roleCatalog.readOnlyMirror}</span>
477-
</div>
478-
</td>
479-
<td>
480-
<div className="stack-gap-2">
481-
<span className="cell-primary">{roleEntry.registered_agent_count}</span>
482-
<span className="mono muted">
483-
{roleEntry.locked_agent_count} {agentsPageCopy.roleCatalog.lockedSuffix}
484-
</span>
485-
</div>
486-
</td>
487-
</tr>
488-
))}
489-
</tbody>
490-
</table>
491-
)}
492-
</Card>
493-
</section>
494422
<section className="app-section" aria-labelledby="agents-ops-title">
495423
<div className="section-header">
496424
<div>
@@ -530,7 +458,6 @@ export default async function AgentsPage({ searchParams }: AgentsPageProps) {
530458
</Button>
531459
</form>
532460
</section>
533-
534461
{/* ── Active State Machine ── */}
535462
<section className="app-section" aria-labelledby="agents-state-machine-title">
536463
<div className="section-header">
@@ -788,6 +715,84 @@ export default async function AgentsPage({ searchParams }: AgentsPageProps) {
788715
</Card>
789716
</section>
790717

718+
<RoleConfigControlPlane roleCatalog={roleCatalogAll} />
719+
<section className="app-section" aria-labelledby="agents-role-catalog-title">
720+
<Card asChild>
721+
<details>
722+
<summary className="section-title" id="agents-role-catalog-title">
723+
{agentsPageCopy.roleCatalog.title}
724+
</summary>
725+
<div className="section-header mt-2">
726+
<div>
727+
<p className="mono muted">{agentsPageCopy.roleCatalog.subtitle}</p>
728+
</div>
729+
<div className="toolbar" role="group" aria-label="Role catalog entry">
730+
<Button asChild variant="secondary" aria-label="Go to the full registered agent list">
731+
<Link href="#agents-registered-title">{agentsPageCopy.roleCatalog.fullList}</Link>
732+
</Button>
733+
</div>
734+
</div>
735+
<div className="table-card mt-2">
736+
{payloadWarning ? (
737+
<p className="mono muted">{agentsPageCopy.roleCatalog.registryUnavailable}</p>
738+
) : roleCatalog.length === 0 ? (
739+
<p className="mono muted">{agentsPageCopy.roleCatalog.noMatches}</p>
740+
) : (
741+
<table className="run-table">
742+
<thead>
743+
<tr>
744+
<th scope="col">{agentsPageCopy.roleCatalog.headers.role}</th>
745+
<th scope="col">{agentsPageCopy.roleCatalog.headers.skillsBundle}</th>
746+
<th scope="col">{agentsPageCopy.roleCatalog.headers.mcpBundle}</th>
747+
<th scope="col">{agentsPageCopy.roleCatalog.headers.runtimeBinding}</th>
748+
<th scope="col">{agentsPageCopy.roleCatalog.headers.executionAuthority}</th>
749+
<th scope="col">{agentsPageCopy.roleCatalog.headers.registeredSeats}</th>
750+
</tr>
751+
</thead>
752+
<tbody>
753+
{roleCatalog.map((roleEntry) => (
754+
<tr key={`role-catalog:${roleEntry.role}`}>
755+
<th scope="row">
756+
<div className="stack-gap-2">
757+
<Badge>{roleEntry.role}</Badge>
758+
<span className="muted">{roleEntry.purpose || agentsPageCopy.roleCatalog.noRolePurpose}</span>
759+
</div>
760+
</th>
761+
<td>
762+
<span className="mono muted">
763+
{formatBindingReadModelLabel(roleEntry.role_binding_read_model?.skills_bundle_ref)}
764+
</span>
765+
</td>
766+
<td>
767+
<span className="mono muted">
768+
{formatBindingReadModelLabel(roleEntry.role_binding_read_model?.mcp_bundle_ref)}
769+
</span>
770+
</td>
771+
<td><span className="mono muted">{formatRoleBindingRuntimeSummary(roleEntry.role_binding_read_model)}</span></td>
772+
<td>
773+
<div className="stack-gap-2">
774+
<Badge variant="running">{roleEntry.role_binding_read_model.execution_authority}</Badge>
775+
<span className="muted">{agentsPageCopy.roleCatalog.readOnlyMirror}</span>
776+
</div>
777+
</td>
778+
<td>
779+
<div className="stack-gap-2">
780+
<span className="cell-primary">{roleEntry.registered_agent_count}</span>
781+
<span className="mono muted">
782+
{roleEntry.locked_agent_count} {agentsPageCopy.roleCatalog.lockedSuffix}
783+
</span>
784+
</div>
785+
</td>
786+
</tr>
787+
))}
788+
</tbody>
789+
</table>
790+
)}
791+
</div>
792+
</details>
793+
</Card>
794+
</section>
795+
791796
<section className="app-section" aria-label="Agent pagination navigation (footer)">
792797
<nav className="toolbar" aria-label="Agent pagination navigation (footer)">
793798
<span className="mono muted" role="status" aria-live="polite" aria-atomic="true">

apps/dashboard/app/contracts/page.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ export default async function ContractsPage({
9797
nextActionHintWithoutRun:
9898
"This contract still needs workflow or role triage before you trust it to continue.",
9999
secondaryDetailsSummary: "Execution details",
100+
inspectionDeckTitle: "Inspection archive",
101+
inspectionDeckSubtitle:
102+
"Keep the first screen for triage and next action. Open the archive only when you need payload, permissions, or runtime binding detail.",
100103
};
101104
const { data: contracts, warning } = await safeLoad(fetchContracts, [] as ContractRecord[], "Contract list");
102105
const resolvedSearchParams = (await searchParams) || {};
@@ -255,7 +258,12 @@ export default async function ContractsPage({
255258
</div>
256259
</Card>
257260
) : (
258-
<div className="grid-2">
261+
<Card asChild>
262+
<details className="collapsible">
263+
<summary>{shellCopy.inspectionDeckTitle}</summary>
264+
<div className="collapsible-body">
265+
<p className="mono muted mb-4">{shellCopy.inspectionDeckSubtitle}</p>
266+
<div className="grid-2">
259267
{visibleContracts.map((contract) => {
260268
const payload = contract.payload || {};
261269
const key = contract.path || contract.task_id || contract.run_id || JSON.stringify(payload).slice(0, 40);
@@ -416,7 +424,10 @@ export default async function ContractsPage({
416424
</Card>
417425
);
418426
})}
419-
</div>
427+
</div>
428+
</div>
429+
</details>
430+
</Card>
420431
)}
421432
{filteredContracts.length > limit ? (
422433
<Card>

0 commit comments

Comments
 (0)