create-pmos 0.124.0 → 0.126.0
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 +1 -1
- package/payload/KIT-VERSION +2 -2
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/scripts/nine-stages.js +81 -0
- package/payload/scripts/workflow-state.js +2 -0
- package/payload/state/discoveries.md +1 -1
- package/payload/state/log.md +1 -1
- package/workspace/c3-one-home-test.js +131 -0
- package/workspace/fixtures/initiatives-api/list.json +171 -3
- package/workspace/fixtures/initiatives-api/roadmap.json +228 -4
- package/workspace/fixtures/initiatives-api/sheet.json +57 -1
- package/workspace/fixtures/one-home-baseline.json +83 -0
- package/workspace/guide/briefing-cards.md +40 -0
- package/workspace/guide/index.md +1 -0
- package/workspace/guide/layout.md +2 -0
- package/workspace/lib/engine/nine-stages.js +81 -0
- package/workspace/lib/engine/workflow-state.js +2 -0
- package/workspace/lib/facts-async.js +51 -0
- package/workspace/lib/item-steps.js +52 -0
- package/workspace/lib/jira-status.js +8 -3
- package/workspace/lib/jobs-to-be-done.js +52 -0
- package/workspace/lib/needs-overdue.js +41 -0
- package/workspace/lib/outbox-store.js +87 -0
- package/workspace/lib/pm-items.js +115 -0
- package/workspace/lib/route-modules.js +21 -0
- package/workspace/lib/routes-briefing.js +109 -0
- package/workspace/lib/routes-c3-init.js +53 -0
- package/workspace/lib/routes-c3-life.js +119 -0
- package/workspace/lib/routes-c3-strategy.js +169 -0
- package/workspace/lib/routes-c3-timeline.js +191 -0
- package/workspace/lib/routes-calendar-import.js +60 -0
- package/workspace/lib/routes-calendar-views.js +61 -0
- package/workspace/lib/routes-doc-health.js +79 -0
- package/workspace/lib/routes-doc-sheet.js +169 -0
- package/workspace/lib/routes-forecast.js +85 -0
- package/workspace/lib/routes-hooks.js +121 -0
- package/workspace/lib/routes-initiatives.js +3 -0
- package/workspace/lib/routes-intake.js +24 -0
- package/workspace/lib/routes-outbox.js +97 -0
- package/workspace/lib/routes-pm-items.js +216 -0
- package/workspace/lib/routes-records.js +61 -0
- package/workspace/lib/routes-releases.js +129 -0
- package/workspace/lib/routes-report-facts.js +84 -0
- package/workspace/lib/routes-report-families.js +32 -0
- package/workspace/lib/routes-roadmap-ops.js +141 -0
- package/workspace/lib/routes-runs.js +119 -0
- package/workspace/lib/routes-signals.js +50 -0
- package/workspace/lib/run-answers.js +69 -0
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/c3.html +26 -0
- package/workspace/public/pm/briefing-deck.js +413 -0
- package/workspace/public/pm/briefing-model.js +211 -0
- package/workspace/public/pm/c3-breakdown.js +240 -0
- package/workspace/public/pm/c3-brief.js +278 -0
- package/workspace/public/pm/c3-cal-strategy.js +528 -0
- package/workspace/public/pm/c3-case.js +185 -0
- package/workspace/public/pm/c3-daypart.js +176 -0
- package/workspace/public/pm/c3-demo-brief-ix.js +350 -0
- package/workspace/public/pm/c3-demo-cal-ix.js +74 -0
- package/workspace/public/pm/c3-demo-cal2.js +66 -0
- package/workspace/public/pm/c3-demo-cal3.js +173 -0
- package/workspace/public/pm/c3-demo-data.js +560 -0
- package/workspace/public/pm/c3-demo-init.js +373 -0
- package/workspace/public/pm/c3-demo-life.js +77 -0
- package/workspace/public/pm/c3-demo-misc2.js +46 -0
- package/workspace/public/pm/c3-demo-plan2.js +30 -0
- package/workspace/public/pm/c3-demo-rep-ix.js +126 -0
- package/workspace/public/pm/c3-demo-rep2.js +92 -0
- package/workspace/public/pm/c3-demo-term.js +153 -0
- package/workspace/public/pm/c3-demo-time.js +49 -0
- package/workspace/public/pm/c3-demo-work-ix.js +80 -0
- package/workspace/public/pm/c3-demo-work2.js +13 -0
- package/workspace/public/pm/c3-demo.js +113 -0
- package/workspace/public/pm/c3-doc-sheet.js +314 -0
- package/workspace/public/pm/c3-dock.js +180 -0
- package/workspace/public/pm/c3-docs.js +290 -0
- package/workspace/public/pm/c3-expand.js +96 -0
- package/workspace/public/pm/c3-init-extras.js +170 -0
- package/workspace/public/pm/c3-lifecycle-panels.js +476 -0
- package/workspace/public/pm/c3-misc-panels.js +89 -0
- package/workspace/public/pm/c3-plan-panels.js +163 -0
- package/workspace/public/pm/c3-plan.js +192 -0
- package/workspace/public/pm/c3-roadmap.js +249 -0
- package/workspace/public/pm/c3-sheet.js +107 -0
- package/workspace/public/pm/c3-shell.js +674 -0
- package/workspace/public/pm/c3-signals.js +196 -0
- package/workspace/public/pm/c3-strategy-panels.js +45 -0
- package/workspace/public/pm/c3-swipe.js +230 -0
- package/workspace/public/pm/c3-terminal-data.js +26 -0
- package/workspace/public/pm/c3-terminal-edge.js +91 -0
- package/workspace/public/pm/c3-terminal-icons.js +41 -0
- package/workspace/public/pm/c3-terminal-results.js +141 -0
- package/workspace/public/pm/c3-terminal.js +52 -0
- package/workspace/public/pm/c3-tickets.js +163 -0
- package/workspace/public/pm/c3-timeline.js +201 -0
- package/workspace/public/pm/c3-toast.js +16 -0
- package/workspace/public/pm/calendar-views.js +1014 -0
- package/workspace/public/pm/console3-lift.md +220 -0
- package/workspace/public/pm/console3.css +1547 -0
- package/workspace/public/pm/dest-initiatives.js +16 -3
- package/workspace/public/pm/dest-mywork.js +8 -1
- package/workspace/public/pm/dest-reports.js +47 -1
- package/workspace/public/pm/dest-today.js +55 -1
- package/workspace/public/pm/doc-health-panel.js +110 -0
- package/workspace/public/pm/doc-rail.js +9 -3
- package/workspace/public/pm/lifecycle-lens-model.js +228 -0
- package/workspace/public/pm/lifecycle-lens.js +261 -0
- package/workspace/public/pm/new-initiative-sheet.js +116 -21
- package/workspace/public/pm/outbox-panel.js +125 -0
- package/workspace/public/pm/pm-work-view.js +895 -0
- package/workspace/public/pm/releases-view.js +248 -0
- package/workspace/public/pm/report-families-view.js +503 -0
- package/workspace/public/pm/run-card.js +244 -0
- package/workspace/public/pm/sessions-panel.js +149 -0
- package/workspace/public/pm/shell-bell.js +162 -0
- package/workspace/public/pm/shell-model.js +83 -0
- package/workspace/public/pm/shell-phone.js +124 -0
- package/workspace/public/pm/shell-readaloud.js +53 -0
- package/workspace/public/pm/shell-recording.js +110 -0
- package/workspace/public/pm/shell-ticker.js +90 -0
- package/workspace/public/pm/slide-confirm.js +163 -0
- package/workspace/public/pm/timeline-forecast-view.js +164 -0
- package/workspace/public/pm/today-day.js +8 -2
- package/workspace/public/pm/tokens.css +1 -0
- package/workspace/server.js +5 -2
- package/workspace/test-files.json +21 -3
- package/workspace/test-lists/browser-suites-green.json +5 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/* pm/c3-demo-brief-ix.js — DEMO ONLY (/c3.html?demo=1): the case files behind the Briefing's cards, needs and drafts, and a simulated agent, so a press on the page does what
|
|
2
|
+
* the mockup's does (console3.html CARDC 2215, NEEDC 2249, DRAFTC 2259, PPL 1053, AM 4957, agent run 5004 on).
|
|
3
|
+
* c3-demo.js loads this module and c3-shell.js waits for it before anything mounts. Real mode never loads it: there, every case is worked out from the engine card
|
|
4
|
+
* (briefing-model.js caseModelOf) and no agent exists, so a row without a route stays a read-only case.
|
|
5
|
+
*
|
|
6
|
+
* What it does, and what it does not:
|
|
7
|
+
* registers the mockup's cases (evidence, options with effects and a confidence, "Unsure", "If you wait"), the people, the playbooks, the documents, the bell's entries
|
|
8
|
+
* decorates GET /api/briefing of the Briefing scope with the item id of each need, the "hard" ones (incident day), the bell entries and the 7 s live arrival
|
|
9
|
+
* simulates the agent's run on a row (`setAgent`): its steps at 750 ms, a stop, a result with labelled claims, and the next moves Accept, Revise, Reject, Define. All in memory.
|
|
10
|
+
* Not simulated (named, not hidden): the permission, plan and question phases of a run, the voice reply, and the failure and plain-text fallbacks (console3.html agRun 5010 to 5021).
|
|
11
|
+
* Writes still reach the fetch wrapper of c3-demo.js, which answers them without saving. Outward options (send, tell someone) are marked `outward: 1`: their slide stays disabled. */
|
|
12
|
+
import { el } from './dom.js';
|
|
13
|
+
import { c3ic } from './pm-work-view.js';
|
|
14
|
+
import { LABELS } from './c3-case.js';
|
|
15
|
+
import { registerCases } from './briefing-model.js';
|
|
16
|
+
import { setSim, setAgent, caseFile, toast } from './c3-brief.js';
|
|
17
|
+
|
|
18
|
+
const O = (id, l, rec, msg, fx, extra) => ({ id, l, rec: rec ? 1 : 0, msg, fx, ...(extra || {}) });
|
|
19
|
+
const K = (sit, conf, chg, opts, extra) => ({ sit, conf, chg, opts, ...(extra || {}) });
|
|
20
|
+
|
|
21
|
+
/* ── the cards (CARDC) ── */
|
|
22
|
+
const DECIDED = { cut: 'cut to 3 flows', keep: 'keep the full scope', add: 'cut and add one engineer' };
|
|
23
|
+
const CARDS = {
|
|
24
|
+
decision: {
|
|
25
|
+
tc: 'var(--destructive)', sit: 'Dara needs the scope before Sprint planning at 14:00. On the full scope Jira shows 3 blockers and the date slips to 31 Oct.', conf: 82, cta: 'Slide to decide',
|
|
26
|
+
chg: [['alert', 'Payments sandbox keys are 2 days late', 'Jira', 'bad'], ['clock', 'The accessibility audit fits 21 Oct only if scope is cut', 'Calendar', 'warn'], ['out', 'Leadership saw 24 Oct on 2 Oct', 'Report']],
|
|
27
|
+
opts: [{ id: 'cut', l: 'Cut to 3 flows', rec: 1, do: 'decide:cut', fx: [['check', 'Holds 24 Oct', 'ok'], ['alert', 'Saved cards move to v2.1', 'warn']] },
|
|
28
|
+
{ id: 'add', l: 'Cut and add one engineer', do: 'decide:add', fx: [['check', 'Holds 24 Oct', 'ok'], ['clock', 'A week of ramp-up', 'warn'], ['user', 'Priya must approve', 'warn']] },
|
|
29
|
+
{ id: 'keep', l: 'Keep the full scope', do: 'decide:keep', fx: [['alert', 'Ships 31 Oct', 'bad'], ['out', 'Breaks the promise', 'bad']] }],
|
|
30
|
+
unsure: "Dara's estimate for flow 4 is a range of 3 to 6 days.", ign: 'Planning starts without scope and the date slips by default.', go: ['goinit', 'checkout', 'Open Checkout v2'],
|
|
31
|
+
doneModel: (id) => ({ tc: 'var(--success)', sit: 'Decided: ' + DECIDED[id] + '. The follow-ups are drafted and nothing has been sent.',
|
|
32
|
+
chg: [['doc', 'PRD section 4 updated', 'draft in Review', 'ok'], ['mail', 'Note to design drafted', 'draft in Review', 'ok'], ['cal', 'Sprint planning is unblocked', '14:00', 'ok']], go: ['goinit', 'checkout', 'Open Checkout v2'] }),
|
|
33
|
+
},
|
|
34
|
+
date: K('The forecast is 7 days past the promise. Scope is the cause, not capacity.', 74,
|
|
35
|
+
[['alert', 'Forecast Oct 31 on the full scope', '', 'warn'], ['target', 'Promised to leadership on 2 Oct', '', 'warn']],
|
|
36
|
+
[O('warn', 'Warn Priya now, with the fix attached', 1, 'Heads-up to Priya drafted. It is in Review.', [['out', 'The draft goes to Review first', 'ok'], ['user', 'Leadership hears it from you', 'ok']], { outward: 1 }),
|
|
37
|
+
O('wait', 'Wait for the 14:00 decision', 0, 'Waiting. Nothing was sent.', [['clock', 'Same news, hours later', 'warn']])],
|
|
38
|
+
{ tc: 'var(--destructive)', cta: 'Slide to send', ign: 'Leadership learns of the slip from the weekly status.', go: ['goinit', 'checkout', 'Open Checkout v2'] }),
|
|
39
|
+
metric: K('Conversion landed at 3.1% at 07:30. That is 0.2 below last week and 0.9 under target.', 71,
|
|
40
|
+
[['bolt', 'Data landed 07:30', 'Analytics'], ['alert', 'The biggest drop is cart to address: 45% stop', 'funnel', 'bad'], ['spark', 'Mobile shows the same pattern, -0.4', 'segment']],
|
|
41
|
+
[O('x', 'Draft an experiment for the address step', 1, 'Experiment brief is drafting. It will appear in Review.', [['spark', 'In Review by noon', 'ok'], ['shield', 'Nothing ships without you', 'ok']]),
|
|
42
|
+
O('w', 'Watch 3 more days', 0, 'Watching. The agent re-checks on Sunday.', [['clock', 'Re-checked Sunday']]),
|
|
43
|
+
O('t', 'Tell Priya in the 10:30 update', 0, 'Added to your 10:30 prep note.', [['out', 'Goes to leadership', 'warn']], { outward: 1 })],
|
|
44
|
+
{ tc: 'var(--acc)', cta: 'Slide to start', unsure: 'One week of data. A seasonal dip is not ruled out.', go: ['gorep', 'product', 'Open the product report'] }),
|
|
45
|
+
insight: K('3 of 3 calls name label printing as the main pain. Interview 3 can confirm it or break it.', 76,
|
|
46
|
+
[['notes', 'Calls 1 to 3 agree on labels', 'Notes', 'ok'], ['alert', 'Only 3 calls: still a lead, not a finding', 'stop rule', 'warn'], ['cal', 'Interview 3 is today at 13:00', 'Calendar']],
|
|
47
|
+
[O('a', 'Add a support-route question to interview 3', 1, 'Question 6 updated in the guide. It is in your list to approve.', [['check', 'Tests the third hypothesis', 'ok'], ['clock', 'Adds 4 minutes']]),
|
|
48
|
+
O('k', 'Keep the guide as it is', 0, 'Guide unchanged.', [['alert', 'Leaves one hypothesis untested', 'warn']])],
|
|
49
|
+
{ tc: 'var(--purple)', cta: 'Slide to add', ign: 'The next chance to ask is interview 4.', go: ['goinit', 'returns', 'Open Returns experience'] }),
|
|
50
|
+
sso: K('The first enterprise-grade feature is live. Leadership has not heard it from you yet, and release is 14 Oct.', 90,
|
|
51
|
+
[['check', '3 customers active, 0 incidents in 7 days', 'Jira', 'ok'], ['target', 'Enterprise pillar: first year-one milestone', 'Strategy', 'ok'], ['shield', '5 of 8 release gates clear', 'Checklist']],
|
|
52
|
+
[O('s', 'Lead the weekly status with it', 1, 'Added as the first line of the status draft.', [['out', 'Reaches leadership Friday', 'ok']]),
|
|
53
|
+
O('n', 'Send Marta and Priya a note now', 0, 'Note drafted. It is in Review.', [['clock', 'Reaches them today']], { outward: 1 }),
|
|
54
|
+
O('h', 'Hold until the release', 0, 'Held until 14 Oct.', [['clock', 'Loses a week of credit', 'warn']])],
|
|
55
|
+
{ tc: 'var(--success)', cta: 'Slide to share', go: ['goinit', 'sso', 'Open SSO'] }),
|
|
56
|
+
search: K('The 6 Oct change cut the 95th percentile latency from 420 ms to 300 ms. It is already past its target of 350 ms.', 93,
|
|
57
|
+
[['check', 'Merged 6 Oct, CI green', 'Repo', 'ok'], ['report', 'Measured across 7 days of traffic', 'Analytics', 'ok']],
|
|
58
|
+
[O('b', "Bank it in Friday's report", 1, 'Added to the Friday report draft.', [['check', 'One line, with the chart', 'ok']]), O('s', 'Skip', 0, 'Skipped.', [['moon', 'Stays in the log']])],
|
|
59
|
+
{ tc: 'var(--success)', cta: 'Slide to bank' }),
|
|
60
|
+
board: K("Three sections wait on numbers. The agent can fill them from Thursday's data if you confirm the two sources.", 79,
|
|
61
|
+
[['report', "Portfolio and outcomes need Thursday's numbers", 'Analytics', 'warn'], ['sheet', 'Investment needs the Q4 forecast', 'Sheets', 'warn'], ['clock', 'Sends 20 Dec', 'Calendar']],
|
|
62
|
+
[O('f', "Let the agent fill from Thursday's numbers", 1, 'Filling three sections. You review them before 14 Dec.', [['check', 'You review before it sends', 'ok']]),
|
|
63
|
+
O('r', 'Review the sections yourself first', 0, 'Sections will wait in your list.', [['clock', 'Adds 40 minutes', 'warn']])],
|
|
64
|
+
{ tc: 'var(--purple)', cta: 'Slide to confirm', go: ['godeliver', 'board', 'Open the board update'] }),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/* ── the needs (NEEDC) ── */
|
|
68
|
+
const NEEDS = {
|
|
69
|
+
i2: K('Priya will quote the 1 Oct conversion figure at 10:30, and two sources disagree.', 90,
|
|
70
|
+
[['user', 'Priya asked in #leadership', 'Slack 07:58'], ['sheet', 'The analytics sheet says 3.1%', 'today', 'ok'], ['doc', 'The PRD says 3.4%', '4 days old', 'warn']],
|
|
71
|
+
[{ id: 'r', l: 'Reply 3.1% and cite the sheet', rec: 1, do: 'done', outward: 1, msg: 'Reply to Priya drafted: 3.1% from the analytics sheet.', fx: [['check', 'Matches the record', 'ok'], ['clock', 'Takes 2 minutes']] },
|
|
72
|
+
{ id: 'a', l: 'Ask Finance to confirm first', do: 'defer', fx: [['alert', 'Misses 10:30', 'bad']] }],
|
|
73
|
+
{ cta: 'Slide to reply', unsure: 'The sheet last refreshed at 06:10.', ign: 'Leadership hears 3.4% at 10:30.' }),
|
|
74
|
+
i3: K('Two workshop actions have no owner. Both are due next week.', 74,
|
|
75
|
+
[['img', 'Read from the whiteboard photo', 'medium confidence', 'warn'], ['user', 'Dara owns the sandbox work', 'Jira'], ['user', 'Lee owns the pricing label', 'Figma']],
|
|
76
|
+
[{ id: 'as', l: 'Sandbox follow-up to Dara, label review to Lee', rec: 1, do: 'done', outward: 1, msg: 'Owners assigned. Dara and Lee are told in drafts.', fx: [['check', 'Matches who owns related work', 'ok']] },
|
|
77
|
+
{ id: 'ask', l: 'Leave open and ask in Sprint planning', do: 'defer', fx: [['clock', 'Decided at 14:00']] }],
|
|
78
|
+
{ cta: 'Slide to assign', unsure: 'Action 2 handwriting was read at medium confidence.' }),
|
|
79
|
+
i4: K('Lee needs one call: the label on the annual plan.', 68,
|
|
80
|
+
[['img', 'One open Figma comment', 'Figma, read Tue', 'warn'], ['doc', 'The pricing page says "Annual" elsewhere', 'PRD']],
|
|
81
|
+
[{ id: 'a', l: '"Save 20% billed yearly"', rec: 1, do: 'done', outward: 1, msg: 'Copy approved. Lee is told in a draft.', fx: [['check', 'Shows the 20% discount from the PRD', 'ok'], ['clock', 'Longer on mobile', 'warn']] },
|
|
82
|
+
{ id: 'b', l: 'Keep "Annual"', do: 'done', outward: 1, msg: 'Kept "Annual". Lee is told in a draft.', fx: [['alert', 'Hides the discount', 'warn']] }],
|
|
83
|
+
{ cta: 'Slide to approve', unsure: 'The Figma file was last read on Tuesday. There may be newer comments.', ign: 'The design handoff waits.' }),
|
|
84
|
+
i5: K('Finance locks the forecast tomorrow with a 6% growth assumption in row 14. The PRD says 4%.', 80,
|
|
85
|
+
[['sheet', 'Forecast row 14: 6%', 'Sheets', 'bad'], ['doc', 'PRD: 4%', 'Confluence'], ['report', 'Last 3 months actual: 3.8%', 'Analytics', 'ok']],
|
|
86
|
+
[{ id: '4', l: 'Set 4% and tell Marta', rec: 1, do: 'done', outward: 1, msg: 'Correction drafted for Marta. Finance sees 4% before the lock.', fx: [['check', 'Matches the PRD and the trend', 'ok']] },
|
|
87
|
+
{ id: '6', l: 'Keep 6%', do: 'done', msg: 'Kept 6%. The PRD is flagged as out of date.', fx: [['alert', 'Needs a new source', 'bad']] }],
|
|
88
|
+
{ cta: 'Slide to correct', ign: 'A 2-point gap flows into the Q4 forecast.' }),
|
|
89
|
+
i6: K('Question 6 of the 13:00 interview guide needs your wording. The agent drafted the rest from the last two calls.', 76,
|
|
90
|
+
[['notes', '2 of 3 calls mentioned the support route', 'Notes'], ['target', 'The stop rule needs a clear yes or no on labels', 'Brief']],
|
|
91
|
+
[{ id: 'o', l: 'Ask openly: "What did you do when printing failed?"', rec: 1, do: 'done', msg: 'Question 6 set. The guide is ready.', fx: [['check', 'Does not lead the answer', 'ok']] },
|
|
92
|
+
{ id: 'd', l: 'Ask directly about contacting support', do: 'done', msg: 'Question 6 set. The guide is ready.', fx: [['alert', 'Leads the answer', 'warn']] }],
|
|
93
|
+
{ cta: 'Slide to approve', ign: 'You open the interview with a blank question.' }),
|
|
94
|
+
i7: K('Marta sent five times. The agent held three that avoid your standing meetings.', 88,
|
|
95
|
+
[['cal', '3 slots held, none overlapping', 'Calendar', 'ok'], ['user', 'Marta replies within a day', 'Gmail']],
|
|
96
|
+
[{ id: 'a', l: 'Accept the 3 held slots', rec: 1, do: 'done', outward: 1, msg: 'Accepted. Marta gets a confirmation draft.', fx: [['check', 'No conflicts', 'ok']] },
|
|
97
|
+
{ id: 'b', l: 'Ask for other times', do: 'defer', outward: 1, fx: [['clock', 'A day lost', 'warn']] }],
|
|
98
|
+
{ cta: 'Slide to accept' }),
|
|
99
|
+
i8: K('No signal in 9 days. The stop rule asked for two signals by 30 Sep.', 77,
|
|
100
|
+
[['clock', '9 days without movement', 'Jira', 'warn'], ['target', 'Stop rule: 2 signals by 30 Sep, found 0', 'Brief', 'bad'], ['init', 'Returns discovery needs the slot', 'Calendar']],
|
|
101
|
+
[{ id: 'p', l: 'Park and revisit in Q1', rec: 1, do: 'done', msg: 'Parked. It returns to the Q1 planning list.', fx: [['check', 'Frees a discovery slot', 'ok']] },
|
|
102
|
+
{ id: 'c', l: 'Continue 2 more weeks', do: 'defer', fx: [['clock', 'Costs a slot', 'warn']] }],
|
|
103
|
+
{ cta: 'Slide to park', ign: 'It keeps taking a discovery slot.' }),
|
|
104
|
+
};
|
|
105
|
+
const genNeed = (n) => K(n.headline, 60, [['clock', 'Due ' + (n.time || 'soon'), n.src || '']],
|
|
106
|
+
[{ id: 'do', l: 'Handle it', rec: 1, do: 'done', fx: [['check', 'Closes the item', 'ok']] }, { id: 'later', l: 'Not today', do: 'defer', fx: [['moon', 'Back Monday 09:00']] }], { cta: 'Slide to confirm' });
|
|
107
|
+
|
|
108
|
+
/* ── the drafts (DRAFTC and the bodies of S.drafts) ── */
|
|
109
|
+
const OK = (extra) => [{ id: 'ok', l: 'Approve', rec: 1, do: 'ready', fx: [['out', 'Nothing is sent until you send it', 'ok']], ...(extra || {}) }, { id: 'sb', l: 'Send back with a note', do: 'back', fx: [['undo', 'The agent revises it']] }];
|
|
110
|
+
const dcase = (quote, sit, conf, chg, unsure, ign, opts, cta) => ({ quote, sit, conf, cta: cta || 'Slide to approve', chg, unsure, ign, opts: opts || OK() });
|
|
111
|
+
const DRAFTS = {
|
|
112
|
+
d1: dcase("Hi Marta, SSO is on track for 14 Oct. SCIM follows in November (EN-412). I will confirm the exact date after Monday's freeze.\n\nWawan", "You promised Marta a date after Monday's freeze. This reply holds the date and names SCIM for November.", 88,
|
|
113
|
+
[['check', 'SSO release 14 Oct', 'Jira field, today', 'ok'], ['check', 'SCIM in November (EN-412)', 'Jira', 'ok'], ['clock', '"Confirm after the freeze" repeats your promise', 'Slack', 'ok']], 'The freeze could still move the date.', 'Marta waits on a date for her own plan.'),
|
|
114
|
+
d2: dcase("Shipped: SSO beta to 3 customers. In progress: Checkout v2, Search relevance. At risk: Checkout v2 date, pending today's scope decision.", 'The draft says "at risk", but the PRD and Jira show different dates. Hold it until your 14:00 decision and the agent refreshes it.', 85,
|
|
115
|
+
[['alert', 'PRD says 31 Oct, Jira says 24 Oct', 'mismatch', 'bad'], ['check', 'SSO beta live with 3 customers', 'Jira', 'ok'], ['check', 'Conversion 3.1%', 'sheet, today', 'ok'], ['clock', 'The last report is 6 days old', 'report', 'warn']], '', 'The weekly status goes out at 16:30.',
|
|
116
|
+
[{ id: 'hold', l: 'Hold until the decision', rec: 1, do: 'back', msg: 'Held. The agent refreshes it after 14:00.', fx: [['check', 'Goes out with one date', 'ok']] }, { id: 'now', l: 'Approve as drafted', do: 'ready', fx: [['alert', 'Leadership sees "at risk" with no decision', 'bad']] }], 'Slide to hold'),
|
|
117
|
+
d3: dcase('Open since last sync: 2 PRD comments, 3 blockers. Ask: scope cut to 3 flows. Last decided: defer saved cards.', 'A prep note for you only. It lists what stayed open since the last sync.', 92,
|
|
118
|
+
[['check', '2 PRD comments open', 'Confluence', 'ok'], ['check', '3 blockers open', 'Jira', 'ok'], ['scale', 'Ask: cut to 3 flows', 'your decision']], '', ''),
|
|
119
|
+
d4: dcase('Duplicates: EN-431, 433, 434, 438, 440, 441 (of EN-402). Need an owner: EN-435, EN-437, EN-439.', 'Six tickets are duplicates of EN-402 and three have no owner. Jira can restore closed tickets for 30 days.', 86,
|
|
120
|
+
[['check', 'Same stack trace as EN-402', '6 of 6', 'ok'], ['user', 'Owners picked by component', 'Jira', 'ok']], 'EN-440 has one extra comment that may add detail.', ''),
|
|
121
|
+
d5: dcase('Label printing is the main pain; customers do not know the 30-day window; support is a workaround. Evidence: 1 call.', 'One call is a lead, not a finding. The summary says so, so no one over-reads it.', 80,
|
|
122
|
+
[['pdf', '1 call, 3 speakers not named', 'Drive', 'warn'], ['check', 'Quotes match the transcript', 'p.3', 'ok']], 'The speakers are not identified.', ''),
|
|
123
|
+
d6: dcase('8 steps, 2 decisions. No retry state drawn.', 'The whiteboard photo was read at medium confidence. The diagram shows what was read, not what was meant.', 70,
|
|
124
|
+
[['img', '8 steps, 2 decisions', 'read medium', 'warn'], ['alert', 'No retry state is drawn', '', 'warn']], 'Handwriting on step 5.', ''),
|
|
125
|
+
d7: dcase('Ship date: 24 Oct (Jira field).', 'Jira holds the ship date as a field, and a field beats prose. The PRD still says 31 Oct.', 84,
|
|
126
|
+
[['check', 'Jira date field: 24 Oct', 'today', 'ok'], ['doc', 'PRD v5: 31 Oct', '4 days old', 'warn']], 'If the cut is not approved, 24 Oct is wrong.', 'Leadership sees two dates.'),
|
|
127
|
+
d8: dcase('Baseline: 3.1% (analytics sheet).', 'The analytics sheet is the record for conversion. The PRD still says 3.4%.', 90,
|
|
128
|
+
[['sheet', 'Sheet: 3.1%', 'today', 'ok'], ['doc', 'PRD: 3.4%', '4 days old', 'warn']], '', 'The weekly report quotes a number the PRD contradicts.'),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const PEOPLE = {
|
|
132
|
+
marta: { n: 'Marta', role: 'Customer success', tier: 'Customer', last: 'Mon 5 Oct', told: 'SSO 14 Oct', owe: [{ t: 'Reply to Marta: SSO 14 Oct, SCIM in Nov', due: 'today', init: 'SSO' }], theirs: [{ t: 'Pick interview slots with 3 customers', due: 'this week', init: 'Returns experience' }] },
|
|
133
|
+
dara: { n: 'Dara', role: 'Engineering lead', tier: 'Colleague', last: 'Wed 7 Oct', told: 'Ship 24 Oct', owe: [{ t: 'Cut Checkout v2 to 3 flows to hold 24 Oct?', due: 'today 14:00', init: 'Checkout v2' }], theirs: [{ t: 'Payments sandbox keys', due: '2 days late', init: 'Checkout v2', overdue: true }] },
|
|
134
|
+
lee: { n: 'Lee', role: 'Design', tier: 'Colleague', last: 'Tue 6 Oct', told: 'Copy freeze 9 Oct', owe: [{ t: 'Approve pricing-page copy before handoff', due: 'Fri', init: 'Checkout v2' }], theirs: [] },
|
|
135
|
+
priya: { n: 'Priya', role: 'VP Product', tier: 'Leadership', last: 'Fri 2 Oct', told: 'Ship 24 Oct, on track (now outdated)', owe: [{ t: 'Confirm the 1 Oct numbers before the 10:30 update', due: '10:30', init: 'Checkout v2' }, { t: 'Weekly status for leadership', due: 'today', init: 'Checkout v2' }], theirs: [] },
|
|
136
|
+
sam: { n: 'Sam', role: 'Engineer', tier: 'Colleague', last: 'Tue 6 Oct', told: '-', owe: [{ t: 'Say yes to Sam: ship the logging change', due: 'today', init: 'Search relevance' }], theirs: [] },
|
|
137
|
+
};
|
|
138
|
+
const PLAYBOOKS = [
|
|
139
|
+
{ id: 'b1', n: 'Keep the breakdown current', trig: 'A PR merges, a ticket changes, a note is saved', first: 'Reconcile items with evidence. Propose new items and re-dates.', mode: 'approve', runs: 34, asis: 88, out: false, gate: 'State changes auto. New items and re-dates need Keep.' },
|
|
140
|
+
{ id: 'b2', n: 'Prepare my meetings', trig: '24 hours before each meeting', first: 'Agenda, what changed, decisions to leave with, pre-read draft.', mode: 'auto', runs: 52, asis: 94, out: true, gate: 'Sending the pre-read is yours.' },
|
|
141
|
+
{ id: 'b3', n: 'Triage the inbox', trig: 'An item arrives', first: 'Estimate effort, attach context, suggest a bucket and a job.', mode: 'approve', runs: 120, asis: 81, out: false, gate: 'Moves to To-Do need one press.' },
|
|
142
|
+
{ id: 'b4', n: 'Weekly update per audience', trig: 'Friday 08:00', first: 'Draft one update per audience and run the checks.', mode: 'approve', runs: 8, asis: 62, out: true, gate: 'Always yours to send.' },
|
|
143
|
+
{ id: 'b5', n: 'Check sources for mismatches', trig: 'Every hour', first: 'Compare sources by the rule and flag differences.', mode: 'auto', runs: 200, asis: 91, out: false, gate: 'Flags only. Fixes need Keep.' },
|
|
144
|
+
{ id: 'b6', n: 'Watch team load', trig: 'Every morning', first: 'Compare commitments with calendar capacity.', mode: 'shadow', runs: 6, asis: 50, out: false, gate: 'Proposals only.' },
|
|
145
|
+
];
|
|
146
|
+
const DOCS = [{ n: 'checkout-v2-prd.md', icon: 'doc' }, { n: 'returns-research-plan.md', icon: 'doc' }, { n: 'customer-call-oct6.pdf', icon: 'pdf' }, { n: 'whiteboard-flow.jpg', icon: 'img' }, { n: 'forecast-q4.xlsx', icon: 'sheet' }];
|
|
147
|
+
const NOTIFS = [
|
|
148
|
+
{ key: 'n1', tag: 'Critical', icon: 'alert', headline: 'Sprint planning at 14:00 is waiting on your scope decision', when: '07:55', unread: true, go: { card: 'decision' } },
|
|
149
|
+
{ key: 'n2', tag: 'Just in', icon: 'bolt', headline: 'Priya asked for the 1 Oct numbers in #leadership', when: '07:58', unread: true, go: { need: 'i2' } },
|
|
150
|
+
{ key: 'n3', tag: 'Opportunity', icon: 'spark', headline: '3 drafts are ready for review', when: '08:05', go: { sheet: 'review' } },
|
|
151
|
+
{ key: 'n4', tag: 'Small win', icon: 'up', headline: 'Search relevance is 28% faster', when: 'Tue', go: { card: 'search' } },
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
/* ── the agent's moves on a row (AM) ── */
|
|
155
|
+
const AM = {
|
|
156
|
+
i2: { label: 'Reply 3.1% and cite the sheet', branch: 'main', steps: ['Reading the Slack thread (quarantined)', 'Reading the analytics sheet', 'Drafting the reply'],
|
|
157
|
+
res: { k: 'draft', tag: 'Just in', h: 'Reply to Priya drafted: 3.1%, from the sheet', draft: 'Hi Priya, the 1 Oct conversion is 3.1% (analytics sheet, refreshed 06:10). The PRD says 3.4% but it is 4 days old; I will correct it today.', out: 'Priya, Slack #leadership',
|
|
158
|
+
claims: [['fact', '3.1% from the analytics sheet, today 06:10'], ['fact', 'The PRD says 3.4%, last edited 4 days ago'], ['est', 'The sheet may refresh again before 10:30']] },
|
|
159
|
+
ev: { steps: ['Opening the analytics sheet'], res: { k: 'ev', tag: 'Just in', h: 'Where 3.1% comes from', claims: [['fact', 'Sheet "Checkout KPIs", cell B14: 3.1%, refreshed 06:10'], ['rep', 'Priya asked in #leadership at 07:58 (imported, untrusted)'], ['ass', 'Priya means conversion, not checkout starts. Not confirmed.']] } },
|
|
160
|
+
alt: { steps: ['Rewriting shorter'], res: { k: 'draft', tag: 'Just in', h: 'A shorter reply to Priya', draft: 'Priya, 1 Oct conversion is 3.1% (analytics sheet, 06:10). The PRD figure is out of date.', out: 'Priya, Slack #leadership', claims: [['fact', '3.1% from the analytics sheet, today 06:10']] } } },
|
|
161
|
+
i3: { label: 'Sandbox follow-up to Dara, label review to Lee', branch: 'main', steps: ['Reading the whiteboard photo', 'Matching actions to owners', 'Drawing the owner map'],
|
|
162
|
+
res: { k: 'diagram', tag: 'Just in', h: 'Owners for the two workshop actions', claims: [['est', 'Action 2 was read at medium confidence (handwriting)'], ['fact', 'Dara owns the sandbox work in Jira (CHK-141)'], ['fact', 'Lee owns the pricing label in Figma']] },
|
|
163
|
+
ev: { steps: ['Reading the photo again'], res: { k: 'ev', tag: 'Just in', h: 'What was read from the photo', claims: [['est', 'Action 1: sandbox follow-up, read at high confidence'], ['est', 'Action 2: label review, read at medium confidence'], ['rep', 'Both due next week, from the whiteboard']] } } },
|
|
164
|
+
i4: { label: '"Save 20% billed yearly"', branch: 'main', steps: ['Reading the Figma comment', 'Reading the PRD pricing section', 'Writing the options'],
|
|
165
|
+
res: { k: 'deck', tag: 'Opportunity', h: 'Three slides for the pricing-copy call', slides: [{ t: 'Where we are', b: [['fact', 'One open Figma comment on the annual-plan label'], ['fact', 'The PRD says "Annual" elsewhere'], ['est', 'The label is the last blocker for handoff']] }, { t: 'Options', b: [['fact', '"Save 20% billed yearly" shows the discount'], ['est', 'Longer on mobile, one line wraps'], ['ass', 'Customers read "Annual" as a price rise']] }, { t: 'Ask', b: [['fact', 'Approve one label by Friday']] }] },
|
|
166
|
+
ev: { steps: ['Reading the Figma file'], res: { k: 'ev', tag: 'Opportunity', h: 'What Figma says', claims: [['fact', 'One open comment, from Lee on Tue'], ['est', 'The file was last read Tue 17:10, so newer comments may exist']] } } },
|
|
167
|
+
i5: { label: 'Set 4% and tell Marta', branch: 'main', steps: ['Reading forecast row 14', 'Reading the PRD section', 'Comparing with 3 months of actuals'],
|
|
168
|
+
res: { k: 'table', tag: 'Critical', h: 'The growth assumption has three sources and two disagree', cols: ['Source', 'Value %', 'Age (days)', 'Status'], rows: [['Forecast, row 14', 6, 3, 'disagrees'], ['PRD section 4', 4, 4, 'matches trend'], ['Actual, last 3 months', 3.8, 0, 'fact'], ['Finance lock', 0, 1, 'tomorrow']],
|
|
169
|
+
claims: [['fact', 'Actual growth over 3 months is 3.8%'], ['est', 'Setting 4% leaves a gap of 0.2 points, 2 either way'], ['ass', 'Finance locks the forecast tomorrow as planned']] },
|
|
170
|
+
ev: { steps: ['Reading the sheet history'], res: { k: 'ev', tag: 'Critical', h: 'Where 6% came from', claims: [['fact', 'Row 14 was last edited 3 days ago by Finance'], ['rep', 'The edit note says "growth target" (imported text, untrusted)'], ['est', 'The 6% matches the target, not the trend']] } } },
|
|
171
|
+
i6: { label: 'Ask openly: "What did you do when printing failed?"', branch: 'discovery/returns-guide', steps: ['Reading the last two calls', 'Reading the guide', 'Rewriting question 6', 'Saving the guide'],
|
|
172
|
+
res: { k: 'cu', tag: 'Small win', h: 'Question 6 is set and the guide is ready', claims: [['fact', 'Question 6 now reads "What did you do when printing failed?"'], ['est', 'The open wording does not lead the answer'], ['fact', 'Written to discovery/returns-guide, pushed to its own branch']], chip: 'Guide ready · branch pushed' },
|
|
173
|
+
ev: { steps: ['Reading the last two calls'], res: { k: 'ev', tag: 'Opportunity', h: 'Why question 6 needs your wording', claims: [['fact', '2 of 3 calls mentioned the support route'], ['rep', '"we just call support" (call 2, imported)'], ['ass', 'A direct question about support may lead the answer']] } } },
|
|
174
|
+
i7: { label: 'Accept the 3 held slots', branch: 'main', steps: ['Reading the calendar', 'Writing the slot brief'],
|
|
175
|
+
res: { k: 'file', tag: 'Just in', h: 'A one-page brief on the three held slots', file: { title: 'Interview slots', path: 'outputs/r14/slots.md', sha: 'c91d02', body: ['Slots held', '- Fri 10:00 with customer A', '- Fri 11:30 with customer B', '- Mon 09:00 with customer C', 'Checked', '- None overlaps a standing meeting', 'Not checked', '- Whether each customer accepts the time'] } },
|
|
176
|
+
ev: { steps: ['Reading the calendar'], res: { k: 'ev', tag: 'Just in', h: 'Why these three slots', claims: [['fact', '3 slots held, none overlapping (calendar, now)'], ['rep', 'Marta sent five times (Gmail, imported)']] } } },
|
|
177
|
+
i8: { label: 'Park and revisit in Q1', branch: 'main', steps: ['Reading the stop rule', 'Marking it parked', 'Adding the Q1 line'],
|
|
178
|
+
res: { k: 'cu', tag: 'Small win', h: 'Parked, with a line in the Q1 plan', claims: [['fact', 'Stop rule asked for 2 signals by 30 Sep, found 0'], ['fact', 'One line added to the Q1 planning list'], ['est', 'Frees one discovery slot']], chip: 'Parked · Q1 line added' },
|
|
179
|
+
ev: { steps: ['Reading the history'], res: { k: 'ev', tag: 'Opportunity', h: 'Why it was kept this long', claims: [['fact', '9 days without movement (Jira)'], ['rep', 'The brief said "2 signals by 30 Sep" (imported)']] } } },
|
|
180
|
+
i15: { label: 'Open the logging change as a pull request', branch: 'initiative/search-relevance', steps: ['Reading the change', 'Running lint locally', 'Opening the pull request'],
|
|
181
|
+
res: { k: 'cu', tag: 'Small win', h: 'Logging change is up for review', claims: [['fact', 'Local lint passes'], ['fact', 'Pushed 5c1d7aa to initiative/search-relevance (observed)'], ['fact', 'Pull request opened; it merges when required CI is green']], chip: 'Pull request · merges when green' },
|
|
182
|
+
ev: { steps: ['Reading the change'], res: { k: 'ev', tag: 'Just in', h: 'What the change touches', claims: [['fact', 'Adds one log line in src/rank.js'], ['est', 'No behaviour change, low risk']] } } },
|
|
183
|
+
};
|
|
184
|
+
const LABEL = { move: 'Acting on the suggested move', ev: 'Asking for evidence', alt: 'Showing other options', revise: 'Revising', free: 'Answering your note' };
|
|
185
|
+
const GL = { fact: 'Fact: read from a source that is named here.', estimate: 'Estimate: worked out from facts, with a range.', assumption: 'Assumption: not checked. It is only as good as who said it.', reported: 'Reported: said by someone, imported, not verified.' };
|
|
186
|
+
const REJ = ['Wrong scope', 'Not enough evidence', 'Wrong tone'];
|
|
187
|
+
const REV = ['shorter', 'warmer', 'more evidence'];
|
|
188
|
+
const TAGC = { Critical: 'var(--destructive)', 'Just in': 'var(--acc)', Opportunity: 'var(--purple)', 'Small win': 'var(--success)', 'Big win': 'var(--success)' };
|
|
189
|
+
|
|
190
|
+
/* ── the simulated agent ── */
|
|
191
|
+
const A = { sx: {}, out: {}, tray: {}, hosts: {}, deck: {}, corr: 0 };
|
|
192
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
193
|
+
const pill = (label, icon, attrs, on) => el('button', { type: 'button', class: 'pill', 'aria-pressed': on === undefined ? null : String(!!on), ...(attrs || {}) }, icon ? c3ic(icon, 'sm') : null, label);
|
|
194
|
+
|
|
195
|
+
function claims(a) {
|
|
196
|
+
return el('ul', { class: 'chgl', role: 'list' }, ...a.map(([lab, text]) => { const L = LABELS[lab] || LABELS.unl; return el('li', { class: L[2] }, c3ic(L[0], 'sm'), el('span', null, text), el('span', { class: 'tag' }, L[1])); }));
|
|
197
|
+
}
|
|
198
|
+
const redraw = (k) => { const h = A.hosts[k]; if (h && h.api) h.api.redraw(); };
|
|
199
|
+
function strip(k) {
|
|
200
|
+
const s = A.sx[k];
|
|
201
|
+
document.querySelectorAll('[data-sxs="' + k + '"]').forEach((e) => {
|
|
202
|
+
e.replaceChildren(); e.classList.toggle('warn', false);
|
|
203
|
+
if (s) { const i = Math.min(s.i, s.steps.length - 1); e.append(el('span', { class: 'c3spin' }, c3ic('spark', 'sm')), s.steps[i] + ' · ' + (i + 1) + ' of ' + s.steps.length); }
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function draw(k) {
|
|
208
|
+
const h = A.hosts[k]; if (!h) return;
|
|
209
|
+
const s = A.sx[k]; const o = A.out[k]; const am = AM[k];
|
|
210
|
+
const out = [];
|
|
211
|
+
if (s) {
|
|
212
|
+
const n = s.steps.length; const i = Math.min(s.i, n - 1);
|
|
213
|
+
const wr = s.intent === 'move' && am.branch && am.branch !== 'main';
|
|
214
|
+
out.push(el('div', { class: 'fold a', 'data-ag-work': k }, el('span', { class: 'c3spin' }, c3ic('spark', 'sm')), el('span', { style: 'flex:1;min-width:0' }, s.steps[i]),
|
|
215
|
+
el('button', { type: 'button', class: 'ib sm', title: 'Stop and discard its branch', 'aria-label': 'Stop the agent', 'data-a': 'agstop', onclick: () => stop(k) }, c3ic('stop', 'sm'))),
|
|
216
|
+
el('div', { class: 'pbar' }, el('i', { style: 'width:' + Math.round((i + 1) / n * 100) + '%' })),
|
|
217
|
+
el('div', { class: 'dim mono', style: 'font-size:var(--fs-1)' }, (LABEL[s.intent] || LABEL.free) + ' · ' + (wr ? 'worktree ' + am.branch + ', shared by this initiative' : 'read-only · main tree, no branch')));
|
|
218
|
+
} else if (o) out.push(resultNode(k, o));
|
|
219
|
+
else out.push(el('div', { class: 'dim mono', style: 'font-size:var(--fs-2)' }, c3ic('spark', 'sm'), ' Suggested move: ' + am.label + ' · swipe right, or slide below'));
|
|
220
|
+
if (A.tray[k] && !s) out.push(trayNode(k));
|
|
221
|
+
h.host.replaceChildren(...out);
|
|
222
|
+
strip(k);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function trayNode(k) {
|
|
226
|
+
const am = AM[k]; const chips = [['Ask for evidence', 'ev'], ['Show other options', 'alt']].filter((c) => am[c[1]]);
|
|
227
|
+
const input = el('input', { type: 'text', id: 'c3f-' + k, placeholder: 'Reply to the agent…', 'aria-label': 'Reply to the agent', autocomplete: 'off', onkeydown: (e) => { if (e.key === 'Enter' && input.value.trim()) { A.tray[k] = false; run(k, 'free', input.value.trim()); } else if (e.key === 'Escape') { e.stopPropagation(); A.tray[k] = false; draw(k); } } });
|
|
228
|
+
return el('div', { class: 'chips2', 'data-ag-tray': k }, ...chips.map((c) => pill(c[0], null, { onclick: () => { A.tray[k] = false; run(k, c[1]); } })),
|
|
229
|
+
el('span', { class: 'form', style: 'flex:1;min-width:160px' }, input),
|
|
230
|
+
el('button', { type: 'button', class: 'ib sm', disabled: true, title: 'Speak is off on this page: the voice reply is not simulated here.', 'aria-label': 'Speak' }, c3ic('mic', 'sm')),
|
|
231
|
+
el('button', { type: 'button', class: 'ib sm', 'aria-label': 'Close the reply', title: 'Close the reply', onclick: () => { A.tray[k] = false; draw(k); } }, c3ic('x', 'sm')));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function resultNode(k, o) {
|
|
235
|
+
const col = TAGC[o.tag] || 'var(--acc)';
|
|
236
|
+
const box = el('div', { class: 'c3res2', 'data-ag-result': k }, o.tag ? el('span', { class: 'tg', style: '--tc:' + col }, o.tag) : null, el('b', { style: 'display:block;margin:4px 0' }, o.h));
|
|
237
|
+
if (o.kind === 'draft') {
|
|
238
|
+
box.append(el('blockquote', { class: 'qd' }, o.draft), claims(o.claims || []),
|
|
239
|
+
el('div', { class: 'dim mono', style: 'font-size:var(--fs-2)' }, c3ic('lock', 'sm'), ' Queued for ' + o.out + '. The session cannot send. Your slide sends exactly this text; the workspace computes its hash.'),
|
|
240
|
+
caseFile({ id: 'sd:' + k, sit: 'Nothing has been sent. The text above goes out only when you slide.', conf: 80, cta: 'Slide to send', ign: 'It stays a draft and the item waits.',
|
|
241
|
+
chg: [['lock', 'The session can only queue a draft', 'rule', 'ok']], opts: [{ id: 'send', l: 'Send exactly this text to ' + o.out, rec: 1, outward: 1, fx: [['lock', 'Outward: bound to this exact text', 'warn'], ['out', 'Goes to ' + o.out, 'warn']] }] }));
|
|
242
|
+
} else {
|
|
243
|
+
if (o.kind === 'table') {
|
|
244
|
+
box.append(el('div', { style: 'overflow-x:auto' }, el('table', { class: 'tbl' }, el('tr', null, ...o.cols.map((c) => el('th', null, c))), ...o.rows.map((r) => el('tr', null, ...r.map((v) => el('td', null, String(v))))))));
|
|
245
|
+
}
|
|
246
|
+
if (o.kind === 'deck') {
|
|
247
|
+
const D = A.deck[k] || (A.deck[k] = { i: 0 }); const sl = o.slides[D.i];
|
|
248
|
+
box.append(el('div', { class: 'fold', style: 'display:grid;gap:6px' }, el('b', null, sl.t), claims(sl.b.map((b) => [b[0] || 'unl', b[1]])),
|
|
249
|
+
el('span', { class: 'chips2' }, el('button', { type: 'button', class: 'ib sm', 'aria-label': 'Previous slide', onclick: () => { D.i = Math.max(0, D.i - 1); draw(k); } }, c3ic('prev', 'sm')), el('span', { class: 'dim mono', style: 'font-size:var(--fs-2)' }, (D.i + 1) + ' / ' + o.slides.length),
|
|
250
|
+
el('button', { type: 'button', class: 'ib sm', 'aria-label': 'Next slide', onclick: () => { D.i = Math.min(o.slides.length - 1, D.i + 1); draw(k); } }, c3ic('next', 'sm')))));
|
|
251
|
+
}
|
|
252
|
+
if (o.kind === 'diagram') box.append(el('div', { class: 'dim mono', style: 'font-size:var(--fs-2)' }, c3ic('info', 'sm'), ' The owner map is drawn locally in the mockup; this demo lists what it shows.'));
|
|
253
|
+
if (o.kind === 'file') box.append(el('div', { class: 'fold', style: 'display:grid;gap:6px' }, el('b', null, c3ic('doc', 'sm'), ' ' + o.file.title + ' '), ...o.file.body.map((l) => (l.startsWith('- ') ? el('div', null, l.slice(2)) : el('h4', { class: 'mono dim', style: 'margin:6px 0 0;font-size:var(--fs-1);text-transform:uppercase' }, l))),
|
|
254
|
+
el('span', { class: 'dim mono', style: 'font-size:var(--fs-1)' }, o.file.path + ' · checksum ' + o.file.sha)));
|
|
255
|
+
if (o.claims) box.append(claims(o.claims));
|
|
256
|
+
if (o.chip) box.append(el('div', { class: 'st ok' }, c3ic('check', 'sm'), o.chip));
|
|
257
|
+
}
|
|
258
|
+
if (o.note) box.append(el('div', { class: 'fold', style: 'display:grid;gap:4px' }, o.note));
|
|
259
|
+
if (o.settled) { box.append(el('div', { class: 'st ok' }, c3ic('check', 'sm'), 'Accepted by you. Recorded as your press.')); return box; }
|
|
260
|
+
const M = o.kind === 'draft' ? ['revise', 'reject'] : o.kind === 'note' ? ['define'] : ['accept', 'revise', 'reject', 'define'];
|
|
261
|
+
const MV = { accept: ['check', 'Accept'], revise: ['edit', 'Revise'], reject: ['x', 'Reject'], define: ['doc', 'Define'] };
|
|
262
|
+
box.append(el('div', { class: 'chips2 c3mv' }, ...M.map((m) => pill(MV[m][1], MV[m][0], { 'data-a': 'agnm', onclick: () => move(k, m) }, o.sub === m)),
|
|
263
|
+
o.prev ? el('button', { type: 'button', class: 'chip2 lk', onclick: () => { A.out[k] = o.prev; toast('Undone. A pull request, if one was opened, is closed and its branch removed.'); draw(k); } }, c3ic('undo', 'sm'), 'undo') : null));
|
|
264
|
+
if (o.sub === 'revise') box.append(el('div', { class: 'chips2' }, ...REV.map((m) => pill(m, null, { onclick: () => { o.sub = null; run(k, 'revise', m); } }))));
|
|
265
|
+
if (o.sub === 'reject') box.append(el('div', { class: 'chips2' }, el('span', { class: 'dim mono', style: 'font-size:var(--fs-2)' }, 'why?'), ...REJ.map((m) => pill(m, null, { onclick: () => reject(k, m) }))));
|
|
266
|
+
if (o.sub === 'define') box.append(el('div', { class: 'chips2' }, ...Object.keys(GL).map((g) => pill(g, null, { onclick: () => { o.def = GL[g]; draw(k); } }))), o.def ? el('div', { class: 'dim mono', style: 'font-size:var(--fs-2)' }, o.def) : null);
|
|
267
|
+
return box;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function move(k, m) {
|
|
271
|
+
const o = A.out[k]; if (!o) return;
|
|
272
|
+
if (m === 'accept') { o.settled = 'accepted'; toast('Recorded as your acceptance.', () => { o.settled = null; draw(k); }); }
|
|
273
|
+
else o.sub = o.sub === m ? null : m;
|
|
274
|
+
o.def = ''; draw(k);
|
|
275
|
+
}
|
|
276
|
+
function reject(k, why) {
|
|
277
|
+
const o = A.out[k]; const pv = o.prev; A.corr++;
|
|
278
|
+
A.out[k] = pv || null;
|
|
279
|
+
toast('Rejected: ' + why + '. Counted as a correction.', () => { A.out[k] = o; A.corr--; redraw(k); });
|
|
280
|
+
redraw(k);
|
|
281
|
+
}
|
|
282
|
+
function stop(k) {
|
|
283
|
+
const s = A.sx[k]; if (!s) return;
|
|
284
|
+
s.cancel = true; A.sx[k] = null;
|
|
285
|
+
toast(s.intent === 'move' && AM[k].branch !== 'main' ? 'Stopped. Uncommitted changes in the worktree were discarded.' : 'Stopped. Nothing had changed.');
|
|
286
|
+
redraw(k);
|
|
287
|
+
}
|
|
288
|
+
function tweak(o, mod) {
|
|
289
|
+
if (o.draft) o.draft = mod === 'shorter' ? 'Priya, 1 Oct conversion is 3.1% (analytics sheet, 06:10). The PRD figure is out of date.' : mod === 'warmer' ? 'Hi Priya, thank you for asking. ' + o.draft.replace(/^Hi Priya, /, '') : o.draft + ' (' + mod + ')';
|
|
290
|
+
else if (o.file && mod === 'shorter') o.file.body = o.file.body.slice(0, 4);
|
|
291
|
+
else if (o.claims && mod === 'more evidence') o.claims.push(['fact', 'Source added: plan run 09:12 and ticket event ev_41']);
|
|
292
|
+
}
|
|
293
|
+
async function run(k, intent, text) {
|
|
294
|
+
if (A.sx[k]) return;
|
|
295
|
+
const am = AM[k];
|
|
296
|
+
const s0 = intent === 'move' ? { steps: am.steps, res: am.res } : am[intent] || (intent === 'revise' ? { steps: ['Revising'] } : { steps: ['Reading your note', 'Checking the plan'], res: { k: 'note' } });
|
|
297
|
+
const r = { steps: s0.steps.slice(), i: 0, intent, cancel: false };
|
|
298
|
+
A.sx[k] = r; A.tray[k] = false;
|
|
299
|
+
const api = A.hosts[k] && A.hosts[k].api; if (api) { api.open(); api.redraw(); }
|
|
300
|
+
for (let i = 0; i < r.steps.length; i++) { r.i = i; draw(k); await sleep(750); if (r.cancel) return; }
|
|
301
|
+
let o;
|
|
302
|
+
if (intent === 'revise') { o = A.out[k]; tweak(o, text); A.corr++; }
|
|
303
|
+
else if (intent === 'free') { o = A.out[k] || { kind: 'note', tag: '', h: 'Your note', claims: [] }; o.note = null; o.say = text; o.note = el('div', { style: 'display:grid;gap:4px' }, el('span', { class: 'dim mono' }, 'you'), el('span', null, text), el('span', { class: 'tag', style: 'justify-self:start' }, 'estimate'), el('span', null, 'The margin is one day because the next step starts the day the last one ends (plan run 09:41). Estimate, 2 days either way.')); }
|
|
304
|
+
else { const res = s0.res; o = JSON.parse(JSON.stringify(res)); o.kind = res.k; o.prev = intent === 'move' ? A.out[k] || null : A.out[k] ? A.out[k].prev : null; o.settled = null; o.sub = null; o.def = ''; }
|
|
305
|
+
A.out[k] = o; A.sx[k] = null;
|
|
306
|
+
toast(intent === 'revise' ? 'Revised. Counted as a correction.' : intent === 'move' ? 'Done on the card. The session ended; its branch stays until its pull request merges.' : 'Done on the card. The session ended.');
|
|
307
|
+
redraw(k);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const agent = {
|
|
311
|
+
has: (k) => !!AM[k],
|
|
312
|
+
info: (k) => ({ label: AM[k].label }),
|
|
313
|
+
state: (k) => (A.sx[k] ? 'busy' : A.out[k] ? 'out' : 'idle'),
|
|
314
|
+
mount(k, host, api) { A.hosts[k] = { host, api }; draw(k); },
|
|
315
|
+
run,
|
|
316
|
+
tray(k) { A.tray[k] = true; const h = A.hosts[k]; if (h && h.api) h.api.open(); draw(k); const i = document.getElementById('c3f-' + k); if (i) i.focus(); },
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/* ── the briefing, decorated ── */
|
|
320
|
+
function decorate(j) {
|
|
321
|
+
if (!j || !Array.isArray(j.needs)) return j;
|
|
322
|
+
j.needs = j.needs.map((n) => ({ ...n, itemId: n.key, hard: n.key === 'i2' || n.key === 'i6' }));
|
|
323
|
+
j.needs.forEach((n) => { if (!NEEDS[n.key]) NEEDS[n.key] = genNeed(n); });
|
|
324
|
+
j.notifs = NOTIFS;
|
|
325
|
+
j.live = { tag: 'Just in', headline: 'Marta confirmed the 3 interview slots', go: { need: 'i7' } };
|
|
326
|
+
j.review = j.review && { ...j.review, rows: j.review.rows.map((r) => ({ ...r })) };
|
|
327
|
+
return j;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let done = false;
|
|
331
|
+
export function installBriefIx() {
|
|
332
|
+
if (done) return; done = true;
|
|
333
|
+
const prep = (m) => { for (const k of Object.keys(m)) m[k] = { ...m[k] }; return m; };
|
|
334
|
+
registerCases({ card: prep(CARDS), need: NEEDS, draft: DRAFTS, people: PEOPLE, playbooks: PLAYBOOKS, docs: DOCS });
|
|
335
|
+
setSim(true); setAgent(agent);
|
|
336
|
+
const prev = window.fetch.bind(window);
|
|
337
|
+
window.fetch = async (input, init) => {
|
|
338
|
+
const res = await prev(input, init);
|
|
339
|
+
try {
|
|
340
|
+
const raw = typeof input === 'string' ? input : input instanceof URL ? input.href : input && input.url;
|
|
341
|
+
const u = new URL(raw, location.origin);
|
|
342
|
+
const method = String((init && init.method) || 'GET').toUpperCase();
|
|
343
|
+
if (u.origin === location.origin && u.pathname === '/api/briefing' && method === 'GET' && (!u.searchParams.get('scope') || u.searchParams.get('scope') === 'brief')) {
|
|
344
|
+
const j = await res.clone().json();
|
|
345
|
+
return new Response(JSON.stringify(decorate(j)), { status: 200, headers: { 'content-type': 'application/json' } });
|
|
346
|
+
}
|
|
347
|
+
} catch { /* an unreadable answer goes through as it is */ }
|
|
348
|
+
return res;
|
|
349
|
+
};
|
|
350
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/* pm/c3-demo-cal-ix.js — DEMO fixtures for the meeting card, the Before / During / After stage and the initiative case files of the Calendar
|
|
2
|
+
* and the Briefing's middle column on /c3.html?demo=1. The content is the console3 mockup's own (console3.html: MW line 2364, MEETC line 2580,
|
|
3
|
+
* prepPack line 1563, the pulse rows line 1448). c3-demo.js imports this file for its side effect: it sets window.__c3calix, which
|
|
4
|
+
* calendar-views.js reads when it is present. Real mode never loads this file, so a real calendar shows only what the calendar memory holds.
|
|
5
|
+
* Keys are the base event id (m1 to m5; a demo event id is `m4@2026-10-08`) and the initiative ids of the calendar's gantt rows.
|
|
6
|
+
* Nothing here reaches the server. `outward: true` on an option means it would tell a person: the slide stays disabled (no channel connected). */
|
|
7
|
+
|
|
8
|
+
const fx = (icon, text, tone) => ({ icon, text, tone: tone || '' });
|
|
9
|
+
|
|
10
|
+
const MEET = {
|
|
11
|
+
m1: { init: 'Search relevance', att: ['S', 'D'], prep: 'ready', line: 'Routine. Keeps Search relevance unblocked.', why: 'Routine. The async summary covers it, so skipping gives back 30 minutes.',
|
|
12
|
+
prepRows: [['spark', 'Agenda', 'Proposed: blockers · next steps', true], ['clock', 'Changed since last time', 'EN-418 still blocked on platform', true], ['scale', 'Leave with', 'No decision needed', true], ['doc', 'Read first', 'Nothing new', true], ['user', 'People', 'Sam, Dara', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', true]],
|
|
13
|
+
case: { id: 'meet:m1', tc: 'var(--success)', sit: 'Standup is routine this week. Sam posts an async summary at 09:25 and nothing in it needs you.', conf: 71, cta: 'Slide to skip',
|
|
14
|
+
chg: [['check', 'The summary covers the 3 open tickets', 'Slack', 'ok'], ['clock', 'Gives back 30 minutes of focus time', 'Calendar', 'ok']],
|
|
15
|
+
opts: [{ id: 'skip', l: 'Skip and read the summary', rec: true, msg: 'Skipped. The summary lands in your list at 09:25.', fx: [['clock', 'Back 30 minutes', 'ok']] }, { id: 'att', l: 'Attend as usual', msg: 'Kept.', fx: [['user', 'You can answer EN-418 live']] }], unsure: 'Sam may raise the EN-418 block live.' } },
|
|
16
|
+
m2: { init: 'Checkout v2', att: ['D', 'L'], prep: 'ready', line: 'Checkout v2: 2 PRD comments and 3 blockers to clear before the 14:00 decision.', why: 'PRD comments fell from 5 to 2 since yesterday. It feeds the 14:00 decision.',
|
|
17
|
+
prepRows: [['spark', 'Agenda', 'Proposed: Scope cut · blockers · next steps', true], ['clock', 'Changed since last time', 'PRD comments 5 to 2 · Conversion down 0.2 pts · New blocker: a11y slot', true], ['scale', 'Leave with', 'Scope cut', true],
|
|
18
|
+
['doc', 'Read first', 'checkout-v2-prd.md · customer-call-oct6.pdf · whiteboard-flow.jpg · forecast-q4.xlsx', true], ['user', 'People', 'Dara owes 1 · Lee owes 1', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
|
|
19
|
+
case: { id: 'meet:m2', tc: 'var(--acc)', sit: 'Prep is ready. Two PRD comments and three blockers are the agenda, and the scope cut is the ask.', conf: 84, cta: 'Slide to confirm',
|
|
20
|
+
chg: [['check', 'Agenda and pre-read are ready', 'Agent', 'ok'], ['clock', 'PRD comments fell from 5 to 2', 'Confluence'], ['scale', 'The ask: cut to 3 flows', 'your call']],
|
|
21
|
+
opts: [{ id: 'att', l: 'Attend and lead with the cut', rec: true, msg: 'Kept. The pre-read goes out when you send it.', fx: [['check', 'Gets the 14:00 decision early', 'ok']] },
|
|
22
|
+
{ id: 'short', l: 'Shorten to 15 minutes', outward: true, msg: 'Shortened to 15 minutes.', fx: [['clock', 'Saves 15 minutes', 'ok'], ['alert', 'Drops the blocker review', 'warn']] },
|
|
23
|
+
{ id: 'written', l: 'Send Dara the ask in writing', outward: true, msg: 'Skipped.', fx: [['clock', 'Saves 45 minutes', 'ok'], ['alert', 'Loses the live debate', 'warn']] }] } },
|
|
24
|
+
m3: { init: 'Returns experience', att: ['M', 'L'], prep: 'review', line: 'Returns discovery: interview 3 of 5. Question 6 needs your wording.', why: 'Interview 3 of 5 tests the label hypothesis. Question 6 needs your wording.',
|
|
25
|
+
prepRows: [['spark', 'Agenda', 'Proposed: interview guide · question 6', true], ['clock', 'Changed since last time', '2 of 3 calls mention the support route', true], ['scale', 'Leave with', 'Park or continue Returns', true],
|
|
26
|
+
['doc', 'Read first', 'interview-guide.md', true], ['user', 'People', 'Marta confirmed the slot', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
|
|
27
|
+
case: { id: 'meet:m3', tc: 'var(--purple)', sit: 'The only meeting that tests a hypothesis. The guide is drafted, and Question 6 needs your wording before 13:00.', conf: 88, cta: 'Slide to confirm',
|
|
28
|
+
chg: [['notes', '2 of 3 calls mention the support route', 'Notes'], ['eye', 'The guide needs your look', 'Review', 'warn'], ['user', 'Marta confirmed the slot', 'Gmail', 'ok']],
|
|
29
|
+
opts: [{ id: 'att', l: 'Attend and approve the guide first', rec: true, msg: 'Kept. Guide review is first in your list.', fx: [['check', 'Tests the third hypothesis', 'ok']] },
|
|
30
|
+
{ id: 'move', l: 'Move to tomorrow', outward: true, msg: 'Move requested.', fx: [['alert', 'Loses the 5-interview window', 'bad']] }] } },
|
|
31
|
+
m4: { init: 'Checkout v2', att: ['D', 'L', 'S'], prep: 'blocked', line: 'Checkout v2: the sprint cannot start until scope is decided. The 24 Oct date depends on it.', why: 'The sprint cannot start until scope is decided. The 24 Oct date depends on it.',
|
|
32
|
+
prepRows: [['spark', 'Agenda', 'Proposed: Scope cut · blockers · next steps', true], ['clock', 'Changed since last time', 'PRD comments 5 to 2 · Conversion down 0.2 pts · New blocker: a11y slot', true], ['scale', 'Leave with', 'Scope cut', true],
|
|
33
|
+
['doc', 'Read first', 'checkout-v2-prd.md · customer-call-oct6.pdf · whiteboard-flow.jpg · forecast-q4.xlsx', true], ['user', 'People', 'Dara owes 1 · Lee owes 1', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
|
|
34
|
+
case: { id: 'meet:m4', tc: 'var(--destructive)', sit: 'Blocked on your scope decision. If it slips past 14:00, the sprint plans the wrong scope.', conf: 78, cta: 'Slide to keep the slot',
|
|
35
|
+
chg: [['alert', 'Waiting on your scope decision', 'Decision', 'bad'], ['user', 'Dara, Lee and Sam are invited', 'Calendar'], ['clock', 'You are free again at 15:30', 'Calendar']],
|
|
36
|
+
opts: [{ id: 'att', l: 'Keep 14:00 and decide before', rec: true, msg: 'Kept. The decision is first in your list.', fx: [['check', 'Planning starts on time', 'ok']] },
|
|
37
|
+
{ id: 'move', l: 'Move to 15:30 if undecided', outward: true, msg: 'Move to 15:30 queued if you have not decided by 13:30.', fx: [['clock', '90 more minutes to decide', 'warn']] }], ign: 'The sprint starts without scope.' } },
|
|
38
|
+
m5: { init: 'Checkout v2', att: ['P', 'D'], prep: 'ready', line: 'You report the date to leadership. The status draft waits for the decision.', why: 'You report the date to leadership. SSO is a milestone worth leading with.',
|
|
39
|
+
prepRows: [['spark', 'Agenda', 'Proposed: date · SSO · risks', true], ['clock', 'Changed since last time', 'SSO beta live with 3 customers', true], ['scale', 'Leave with', 'The date, said plainly', true], ['doc', 'Read first', 'status-draft.md', true], ['user', 'People', 'Priya', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
|
|
40
|
+
case: { id: 'meet:m5', tc: 'var(--success)', sit: 'You report the date to leadership. The status draft waits for the scope decision, and SSO is worth leading with.', conf: 80, cta: 'Slide to confirm',
|
|
41
|
+
chg: [['report', 'The status draft is in Review', 'Review', 'warn'], ['flag', 'SSO beta live with 3 customers', 'Jira', 'ok'], ['alert', '24 Oct is conditional until 14:00', 'Decision', 'warn']],
|
|
42
|
+
opts: [{ id: 'att', l: 'Attend and lead with SSO', rec: true, msg: 'Kept. SSO leads the status draft.', fx: [['check', 'Opens with the win', 'ok']] },
|
|
43
|
+
{ id: 'written', l: 'Send the update in writing', outward: true, msg: 'Skipped.', fx: [['clock', 'Saves 30 minutes', 'ok'], ['alert', 'No live questions', 'warn']] }] } },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/* the initiative case files behind a pulse row, a gantt row or a dot (the mockup opens the initiative's detail; here it is a sheet) */
|
|
47
|
+
const INIT = {
|
|
48
|
+
checkout: { tc: 'var(--destructive)', sit: 'Forecast Oct 31 against promised Oct 24: 7 days late on the full scope. Dara needs the scope decision before Sprint planning at 14:00.', conf: 82, cta: 'Slide to decide',
|
|
49
|
+
chg: [['alert', 'Forecast Oct 31, promised Oct 24 (+7 days)', 'Forecast', 'bad'], ['alert', '3 blockers on the full scope', 'Jira', 'warn'], ['clock', 'Sprint planning is at 14:00', 'Calendar']],
|
|
50
|
+
opts: [{ id: 'cut', l: 'Cut to 3 flows and hold 24 Oct', rec: true, msg: 'Decision logged. Follow-up drafts are in Review.', fx: [['check', '24 Oct holds', 'ok'], ['alert', '2 flows move to v2.1', 'warn']] }, { id: 'full', l: 'Keep the full scope, move to 31 Oct', outward: true, msg: 'Queued.', fx: [['alert', 'Leadership is told the date moved', 'bad']] }],
|
|
51
|
+
unsure: 'The a11y audit slot may still move.', ign: 'The sprint starts without scope.', go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
|
|
52
|
+
sso: { tc: 'var(--warning)', sit: '3 of 8 release checks are open and the release is 6 days away. The date holds if the checks close by Monday.', conf: 74, cta: 'Slide to confirm',
|
|
53
|
+
chg: [['clock', '3 of 8 release checks open', 'Jira', 'warn'], ['check', 'Beta is live with 3 customers', 'Jira', 'ok']],
|
|
54
|
+
opts: [{ id: 'hold', l: 'Hold 14 Oct and watch the checks', rec: true, msg: 'Kept. The checks are on your Monday list.', fx: [['check', 'No change to the promise', 'ok']] }, { id: 'tell', l: 'Warn Marta the date may move', outward: true, msg: 'Queued.', fx: [['user', 'Marta hears it early']] }],
|
|
55
|
+
go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
|
|
56
|
+
search: { tc: 'var(--warning)', sit: 'EN-418 has been blocked on the platform team with no reply since Tuesday. Search relevance is at risk, not late.', conf: 69, cta: 'Slide to confirm',
|
|
57
|
+
chg: [['alert', 'EN-418 blocked, no reply since Tue', 'Jira', 'warn'], ['up', 'Relevance is 28% faster on the index so far', 'Report', 'ok']],
|
|
58
|
+
opts: [{ id: 'ask', l: 'Ask the platform lead for a date', rec: true, outward: true, msg: 'Queued.', fx: [['user', 'A reply by Friday']] }, { id: 'wait', l: 'Wait until Monday', msg: 'Kept.', fx: [['clock', 'Costs 2 more days', 'warn']] }],
|
|
59
|
+
go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
|
|
60
|
+
returns: { tc: 'var(--success)', sit: 'Learning is on schedule. Interview 5 on 13 Oct decides park or continue, and the stop rule has a review date.', conf: 80, cta: 'Slide to confirm',
|
|
61
|
+
chg: [['check', 'Interview 3 of 5 is booked', 'Calendar', 'ok'], ['notes', '2 of 3 calls mention the support route', 'Notes']],
|
|
62
|
+
opts: [{ id: 'keep', l: 'Keep the plan', rec: true, msg: 'Kept.', fx: [['check', 'Interview 5 decides', 'ok']] }],
|
|
63
|
+
go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
|
|
64
|
+
onb: { tc: 'var(--muted-foreground)', sit: 'No movement in 9 days. The stop rule found no signals. Continue or park?', conf: 60, cta: 'Slide to confirm',
|
|
65
|
+
chg: [['moon', 'No change in 9 days', 'Repository', 'warn'], ['info', 'No KR or health budget is named', 'Anchor', 'bad']],
|
|
66
|
+
opts: [{ id: 'park', l: 'Park it until Q1', rec: true, msg: 'Parked. Nothing outside changed.', fx: [['moon', 'Frees the next review', 'ok']] }, { id: 'go', l: 'Continue and name an anchor', msg: 'Kept.', fx: [['alert', 'Needs an anchor first', 'warn']] }],
|
|
67
|
+
ign: 'It stays unanchored and is rejected at the gate.', go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/* the year pane's dots name a bet; this maps the name to the initiative whose case file the dot opens */
|
|
71
|
+
const LINK = { 'Checkout v2': 'checkout', 'Search relevance': 'search', 'Returns discovery': 'returns', 'Continue or park?': 'onb', 'SSO release, SCIM': 'sso' };
|
|
72
|
+
|
|
73
|
+
window.__c3calix = { MEET, INIT, LINK };
|
|
74
|
+
export { MEET, INIT, LINK, fx };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* pm/c3-demo-cal2.js — DEMO fixtures for the Calendar's After notes, the Week time grid and the "Progress and timing" card on
|
|
2
|
+
* /c3.html?demo=1. The content is the console3 mockup's own: DECS and ACT (console3.html lines 1492 and 1493), notesView (1494),
|
|
3
|
+
* MT and MS (1517 and 1518: the meetings and the markers of each day), commits() (2705: the dated promises and tasks) and PCT (2703).
|
|
4
|
+
* c3-demo.js imports this file for its side effect: it sets window.__c3cal2, which calendar-views.js reads when it is present.
|
|
5
|
+
* Real mode never loads this file, so a real calendar draws only what the calendar memory holds. Nothing here reaches the server. */
|
|
6
|
+
|
|
7
|
+
const MT = { // Oct 2026, by day of the month: [start, end, title]
|
|
8
|
+
5: [[9.5, 10, 'Standup'], [11, 12, 'Roadmap review'], [14, 15.5, 'Design critique']],
|
|
9
|
+
6: [[9.5, 10, 'Standup'], [13, 14, 'Customer call'], [15, 16, '1:1 Dara'], [16, 17, 'Discovery sync']],
|
|
10
|
+
7: [[9.5, 10, 'Standup'], [10, 12, 'Checkout workshop'], [14, 15, 'Leadership prep'], [15.5, 16.5, 'Sales sync']],
|
|
11
|
+
9: [[9.5, 10, 'Standup'], [11, 12, 'Design review'], [14, 15, '1:1 Priya']],
|
|
12
|
+
12: [[9.5, 10, 'Standup'], [10.5, 11.5, 'SSO freeze'], [14, 15, 'Interview 4'], [15.5, 16, '1:1 Lee']],
|
|
13
|
+
13: [[9.5, 10, 'Standup'], [13, 14, 'Interview 5'], [15, 16, 'Roadmap sync']],
|
|
14
|
+
14: [[9.5, 10, 'Standup'], [10, 11, 'SSO release check'], [14, 15, 'Leadership prep']],
|
|
15
|
+
15: [[9.5, 10, 'Standup'], [11, 12, 'Design review']],
|
|
16
|
+
16: [[9.5, 10, 'Standup'], [12, 13, 'Leadership weekly'], [14, 15, '1:1 Dara']],
|
|
17
|
+
};
|
|
18
|
+
/* Thursday 8 Oct is the page's own day: the five meetings of the mockup's S.meetings (id, start, end, title) */
|
|
19
|
+
const TODAY_MEET = [['m1', 9.5, 10, 'Standup'], ['m2', 10.5, 11.25, 'Checkout sync'], ['m3', 13, 14, 'Customer interview'], ['m4', 14, 15, 'Sprint planning'], ['m5', 16.5, 17, 'Leadership update']];
|
|
20
|
+
const MS = { // markers by date: [kind, title]
|
|
21
|
+
'2026-10-09': [['copy', 'Copy freeze']], '2026-10-12': [['promise', 'SSO date to Marta']], '2026-10-13': [['learn', 'Interview 5: review the stop rule']],
|
|
22
|
+
'2026-10-14': [['ship', 'SSO release']], '2026-10-16': [['report', 'Leadership weekly']], '2026-10-21': [['risk', 'A11y slot, if scope is cut']],
|
|
23
|
+
'2026-10-23': [['report', 'Leadership weekly']], '2026-10-24': [['promise', 'Checkout v2 promised']], '2026-10-28': [['risk', 'A11y slot, full scope']],
|
|
24
|
+
'2026-10-30': [['report', 'Leadership weekly']], '2026-10-31': [['forecast', 'Checkout v2, full scope']], '2026-11-06': [['promise', 'Search relevance promised']],
|
|
25
|
+
};
|
|
26
|
+
const ROUTINE = /^(Standup|1:1|Sync$)/;
|
|
27
|
+
|
|
28
|
+
/** The meetings and markers of one day, as the mockup's dayMeetings and marks draw them. Weekends are empty. */
|
|
29
|
+
function day(date) {
|
|
30
|
+
const [y, m, d] = date.split('-').map(Number);
|
|
31
|
+
const w = new Date(Date.UTC(y, m - 1, d)).getUTCDay();
|
|
32
|
+
const mk = MS[date] || [];
|
|
33
|
+
if (w === 0 || w === 6) return { ms: [], mk };
|
|
34
|
+
if (date === '2026-10-08') return { ms: TODAY_MEET.map(([id, s, e, t]) => ({ id: id + '@' + date, s, e, t, hot: id === 'm4', rt: t === 'Standup' })), mk };
|
|
35
|
+
const a = (m === 10 && MT[d]) || [[9.5, 10, 'Standup'], [14, 15, 'Sync']];
|
|
36
|
+
return { ms: a.map(([s, e, t]) => ({ s, e, t, rt: ROUTINE.test(t) })), mk };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const NOTES = {
|
|
40
|
+
title: 'Checkout workshop', when: 'Wed 7 Oct · 41 min', who: ['Dara', 'Lee', 'Sam'],
|
|
41
|
+
diff: 'live capture says "cut to three flows"; final note says "hold 24 Oct"',
|
|
42
|
+
decisions: [{ id: 'x1', t: 'Defer saved cards to v2.1', rej: 'Keep in v2' }, { id: 'x2', t: 'Hold the 24 Oct date until the scope cut is decided', rej: 'Move the date now' }],
|
|
43
|
+
actions: [{ id: 'a1', t: 'Send the revised scope to design', o: 'You', d: 'Fri' }, { id: 'a2', t: 'Book the a11y audit before 28 Oct', o: '', d: 'Fri' },
|
|
44
|
+
{ id: 'a3', t: 'Confirm sandbox keys with the vendor', o: 'Dara', d: 'Fri' }, { id: 'a4', t: 'Share returns insights with sales', o: '', d: 'next Tue' }],
|
|
45
|
+
owners: ['', 'You', 'Dara', 'Lee', 'Marta'], dues: ['Fri', 'next Tue', 'no date'],
|
|
46
|
+
told: ['Dara', 'Lee', 'Sam'], notTold: ['Priya'], flag: '24 Oct is conditional',
|
|
47
|
+
live: 'Dara: the keys are the real blocker, not the scope.\nYou: then we cut to three flows and keep 24 Oct.\n[highlight] decision: cut to three flows\n[caught] action: Dara confirms sandbox keys',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/* the mockup's commits() tasks (line 2705): dates are 2026 and days are absolute, so they hold when the page's own day is 8 Oct */
|
|
51
|
+
const TASKS = [
|
|
52
|
+
{ id: 't-keys', n: 'Sandbox keys from the vendor', due: '2026-10-06', st: 'over', late: 2, why: 'Asked Tue. No reply in 2 days. Blocks 24 Oct.' },
|
|
53
|
+
{ id: 't-num', n: '1 Oct numbers to Priya', due: '2026-10-08', st: 'ok', why: 'Priya asked at 07:58. Due 10:30.' },
|
|
54
|
+
{ id: 't-wk', n: 'Weekly status to leadership', due: '2026-10-09', st: 'risk', why: 'The draft waits for the 14:00 decision.' },
|
|
55
|
+
{ id: 't-sso', n: 'SSO date to Marta', due: '2026-10-12', st: 'ok', why: "Promised after Monday's freeze." },
|
|
56
|
+
{ id: 't-int5', n: 'Interview 5: review the stop rule', due: '2026-10-13', st: 'ok', why: 'Decides whether returns discovery continues.' },
|
|
57
|
+
{ id: 't-a11y', n: 'A11y audit slot', due: '2026-10-21', st: 'risk', why: 'No slot before 28 Oct unless scope is cut.' },
|
|
58
|
+
{ id: 't-prd', n: 'PRD v5 sent to Dara', due: '2026-10-07', st: 'done', why: 'Sent Wed.' },
|
|
59
|
+
{ id: 't-ssob', n: 'SSO beta live', due: '2026-10-06', st: 'done', why: '3 customers, 0 incidents.' },
|
|
60
|
+
{ id: 't-srch', n: 'Search fix merged', due: '2026-10-06', st: 'done', why: 'p95 420 to 300 ms.' },
|
|
61
|
+
];
|
|
62
|
+
const PCT = { checkout: 0.5, returns: 0.4, sso: 0.75, search: 0.38, onb: 0.2 }; // the mockup's PCT (line 2703)
|
|
63
|
+
|
|
64
|
+
const STAT = { sso: 'risk' }; // the mockup's amber SSO (its rag), where the shared demo row says on time
|
|
65
|
+
|
|
66
|
+
window.__c3cal2 = { day, NOTES, TASKS, PCT, STAT };
|