// Tenney Project DP — Data / Control / Context faces + detail panel. // Split from tenney_project.jsx to keep files small. Loaded AFTER it. /* ── Data face: the plan as an XLSX sheet (framework sheet styles) ── */ function PjData({ P, tasks, fmtD }) { const cols = ['ID', 'Task', 'Workstream', 'Owner', 'Status', 'Priority', 'Start', 'Due', '% Complete', 'Milestone', 'Depends On', 'Notes']; return (
Excel{P.file} Dropbox · Synced {P.synced}
Writes back on edit
{cols.map((c, i) => )} {tasks.map((t, i) => )}
{c}
{i + 1} {t.id} {t.name} {t.ws} {P.people[t.owner]} {t.status} {t.priority} {fmtD(t.start)} {fmtD(t.due)} {t.pct}% {t.milestone ? 'Y' : 'N'} {t.deps.join(', ')} {t.notes}
); } /* ── Control face: the DP's agent + workflows (hub Control vocabulary) ── */ function PjControl({ P }) { const stats = [ ['Sources', '2', 'feed this product', ''], ['In Knowledge Base', '2', 'available to Claude', 'good'], ['Awaiting review', '1', 'updated source', 'warn'], ['Agent', 'On', 'wakes on sync', 'good'], ['Last built', '3 h ago', 'on plan edit', ''], ['Governance', 'Verified', 'read-write · Dropbox', 'good'], ]; const wfs = [ ['Overdue sweep', 'Flags tasks past due and notifies owners each morning', 'Daily · 07:30', 'clock', '5 h ago', '07:30', true], ['Milestone drift alert', 'Runs when a milestone\u2019s projected date slips', 'On date change', 'event', '3 d ago', 'On change', true], ['Plan write-back', 'Writes task edits back to the Dropbox plan file', 'On edit', 'change', '3 h ago', 'On edit', true], ['Blocked-task escalation', 'Escalates tasks blocked more than 5 days to the deal lead', 'Daily · 08:00', 'clock', '5 h ago', '08:00', true], ]; const trigIcon = (kind) => kind === 'event' ? : kind === 'change' ? : ; return (
{stats.map(([l, v, s, cls], i) =>
{l}
{v}
{s}
)}
Data Product Agent

This agent keeps the plan honest: it writes edits back to the plan file, sweeps for overdue and blocked work, and rebuilds the tracker when the file changes outside Proxa.

ArtifactSource updatesLast update
Disposition Plan
plan file · DD workflow · Lease-Up gate
1 3 h ago
Workflows

Recurring jobs this data product owns. Run one immediately without waiting for its schedule.

{wfs.map(([name, desc, trig, kind, last, next, on], i) => )}
WorkflowTriggerLast runNext runStatus
{name}
{desc}
{trigIcon(kind)}{trig} {last} {next} {on ? 'On' : 'Paused'}
); } /* ── Context face: the DP's skill ── */ function PjContext({ P }) { const S = ({ title, html }) => (
{title}

); return (

This artifact is self-describing — its context is its skill: what it is, what feeds it, the rules its content must follow, and how it stays fresh.

); } /* ── Detail panel (task view / new task form) ── */ function PjDetail({ P, task, tasks, fmtD, overdue, onClose, onAdd }) { React.useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, [onClose]); const [form, setForm] = React.useState({ name: '', desc: '', ws: P.workstreams[0], owner: 'SO', priority: 'Medium', start: '2026-02-12', due: '2026-02-27', milestone: false }); const [nameErr, setNameErr] = React.useState(false); return (
); } Object.assign(window, { PjData, PjControl, PjContext, PjDetail });