otacon 0.1.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/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/cli/client.js +188 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/commands/answer.js +63 -0
- package/dist/cli/commands/answer.js.map +1 -0
- package/dist/cli/commands/ask.js +117 -0
- package/dist/cli/commands/ask.js.map +1 -0
- package/dist/cli/commands/clean.js +48 -0
- package/dist/cli/commands/clean.js.map +1 -0
- package/dist/cli/commands/doctor.js +86 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/expose.js +104 -0
- package/dist/cli/commands/expose.js.map +1 -0
- package/dist/cli/commands/implement-done.js +53 -0
- package/dist/cli/commands/implement-done.js.map +1 -0
- package/dist/cli/commands/install.js +113 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/open.js +37 -0
- package/dist/cli/commands/open.js.map +1 -0
- package/dist/cli/commands/progress.js +45 -0
- package/dist/cli/commands/progress.js.map +1 -0
- package/dist/cli/commands/start.js +66 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.js +44 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/submit.js +64 -0
- package/dist/cli/commands/submit.js.map +1 -0
- package/dist/cli/commands/wait.js +66 -0
- package/dist/cli/commands/wait.js.map +1 -0
- package/dist/cli/install/assets.js +285 -0
- package/dist/cli/install/assets.js.map +1 -0
- package/dist/cli/install/locations.js +92 -0
- package/dist/cli/install/locations.js.map +1 -0
- package/dist/cli/install/tailscale.js +39 -0
- package/dist/cli/install/tailscale.js.map +1 -0
- package/dist/cli/main.js +73 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/output.js +39 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/session.js +77 -0
- package/dist/cli/session.js.map +1 -0
- package/dist/daemon/activity.js +56 -0
- package/dist/daemon/activity.js.map +1 -0
- package/dist/daemon/anchor.js +143 -0
- package/dist/daemon/anchor.js.map +1 -0
- package/dist/daemon/app.js +1081 -0
- package/dist/daemon/app.js.map +1 -0
- package/dist/daemon/approve.js +71 -0
- package/dist/daemon/approve.js.map +1 -0
- package/dist/daemon/desktop-notify.js +69 -0
- package/dist/daemon/desktop-notify.js.map +1 -0
- package/dist/daemon/diff.js +187 -0
- package/dist/daemon/diff.js.map +1 -0
- package/dist/daemon/linter/index.js +19 -0
- package/dist/daemon/linter/index.js.map +1 -0
- package/dist/daemon/linter/parse.js +350 -0
- package/dist/daemon/linter/parse.js.map +1 -0
- package/dist/daemon/linter/rules.js +359 -0
- package/dist/daemon/linter/rules.js.map +1 -0
- package/dist/daemon/main.js +48 -0
- package/dist/daemon/main.js.map +1 -0
- package/dist/daemon/notify.js +23 -0
- package/dist/daemon/notify.js.map +1 -0
- package/dist/daemon/presence.js +37 -0
- package/dist/daemon/presence.js.map +1 -0
- package/dist/daemon/queue.js +160 -0
- package/dist/daemon/queue.js.map +1 -0
- package/dist/daemon/store.js +393 -0
- package/dist/daemon/store.js.map +1 -0
- package/dist/daemon/threads.js +153 -0
- package/dist/daemon/threads.js.map +1 -0
- package/dist/daemon/transcript.js +89 -0
- package/dist/daemon/transcript.js.map +1 -0
- package/dist/daemon/ui.js +175 -0
- package/dist/daemon/ui.js.map +1 -0
- package/dist/shared/config.js +93 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/gwt.js +69 -0
- package/dist/shared/gwt.js.map +1 -0
- package/dist/shared/paths.js +67 -0
- package/dist/shared/paths.js.map +1 -0
- package/dist/shared/question-spec.js +44 -0
- package/dist/shared/question-spec.js.map +1 -0
- package/dist/shared/types.js +35 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/shared/version.js +5 -0
- package/dist/shared/version.js.map +1 -0
- package/dist/ui/assets/arc-HhPfdCPZ.js +1 -0
- package/dist/ui/assets/architecture-7EHR7CIX-BPLblcyi.js +1 -0
- package/dist/ui/assets/architectureDiagram-3BPJPVTR-D2PIxGOb.js +36 -0
- package/dist/ui/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/assets/blockDiagram-GPEHLZMM-DQ3Dn17h.js +132 -0
- package/dist/ui/assets/c4Diagram-AAUBKEIU-DxITrQgS.js +10 -0
- package/dist/ui/assets/channel-ipcU8ZNI.js +1 -0
- package/dist/ui/assets/chunk-2J33WTMH-Du1JoPx5.js +1 -0
- package/dist/ui/assets/chunk-3OPIFGDE-Dn7x2Yqf.js +62 -0
- package/dist/ui/assets/chunk-4BX2VUAB-DVnrE-4n.js +1 -0
- package/dist/ui/assets/chunk-55IACEB6-BAhFAimA.js +1 -0
- package/dist/ui/assets/chunk-5ZQYHXKU-0hEZptem.js +2 -0
- package/dist/ui/assets/chunk-727SXJPM-C1FN_cI3.js +206 -0
- package/dist/ui/assets/chunk-AQP2D5EJ-A656OBd4.js +231 -0
- package/dist/ui/assets/chunk-BSJP7CBP-D8oMbjm8.js +1 -0
- package/dist/ui/assets/chunk-CSCIHK7Q-DjIL8GLi.js +122 -0
- package/dist/ui/assets/chunk-FMBD7UC4-Otblfqvz.js +15 -0
- package/dist/ui/assets/chunk-KSCS5N6A-BOjTvm3H.js +10 -0
- package/dist/ui/assets/chunk-L5ZTLDWV-CaTLaw6L.js +1 -0
- package/dist/ui/assets/chunk-LZXEDZCA-Dq5p7qrD.js +2 -0
- package/dist/ui/assets/chunk-ND2GUHAM-jZ_NNnWi.js +1 -0
- package/dist/ui/assets/chunk-NNHCCRGN-DlpIbxXb.js +159 -0
- package/dist/ui/assets/chunk-NZK2D7GU-U_7l_sCh.js +1 -0
- package/dist/ui/assets/chunk-O5CBEL6O-MewqqNB7.js +70 -0
- package/dist/ui/assets/chunk-QZHKN3VN-DzGPH44B.js +1 -0
- package/dist/ui/assets/chunk-WU5MYG2G-DyEIVjoo.js +1 -0
- package/dist/ui/assets/chunk-XPW4576I-D5ArxNEF.js +32 -0
- package/dist/ui/assets/classDiagram-4FO5ZUOK-Byg2Hl9D.js +1 -0
- package/dist/ui/assets/classDiagram-v2-Q7XG4LA2-Byg2Hl9D.js +1 -0
- package/dist/ui/assets/cose-bilkent-S5V4N54A-PFXzf7WV.js +1 -0
- package/dist/ui/assets/cytoscape.esm-h6BdjjI9.js +321 -0
- package/dist/ui/assets/dagre-BM42HDAG-xrCfjZuZ.js +4 -0
- package/dist/ui/assets/dagre-Bx709z4p.js +1 -0
- package/dist/ui/assets/defaultLocale-C8Fc0cco.js +1 -0
- package/dist/ui/assets/diagram-2AECGRRQ-BFf-cyKY.js +43 -0
- package/dist/ui/assets/diagram-5GNKFQAL-kNPV4NfV.js +10 -0
- package/dist/ui/assets/diagram-KO2AKTUF-ByC1IUwG.js +3 -0
- package/dist/ui/assets/diagram-LMA3HP47-DZIJMPK0.js +24 -0
- package/dist/ui/assets/diagram-OG6HWLK6-CSDED9A-.js +24 -0
- package/dist/ui/assets/dist-YwjsDswi.js +1 -0
- package/dist/ui/assets/erDiagram-TEJ5UH35-yuzvjE6J.js +85 -0
- package/dist/ui/assets/eventmodeling-FCH6USID-CZR4eNG-.js +1 -0
- package/dist/ui/assets/flowDiagram-I6XJVG4X-ApPtVyYM.js +162 -0
- package/dist/ui/assets/ganttDiagram-6RSMTGT7-BeMLXtAr.js +292 -0
- package/dist/ui/assets/gitGraph-WXDBUCRP-JmTTBa7j.js +1 -0
- package/dist/ui/assets/gitGraphDiagram-PVQCEYII-Cjjnjs71.js +106 -0
- package/dist/ui/assets/graphlib-B8gBHxth.js +1 -0
- package/dist/ui/assets/index-BFQVRcSI.js +11 -0
- package/dist/ui/assets/index-Bj_kTrwP.css +1 -0
- package/dist/ui/assets/info-J43DQDTF-8vZ3gome.js +1 -0
- package/dist/ui/assets/infoDiagram-5YYISTIA-CnMk1cA-.js +2 -0
- package/dist/ui/assets/init-D6jRqBbL.js +1 -0
- package/dist/ui/assets/ishikawaDiagram-YF4QCWOH-Bl8z6huD.js +70 -0
- package/dist/ui/assets/journeyDiagram-JHISSGLW-DYIVfMpS.js +139 -0
- package/dist/ui/assets/kanban-definition-UN3LZRKU-BnR0ZzOz.js +89 -0
- package/dist/ui/assets/katex-Vhh-h91d.js +257 -0
- package/dist/ui/assets/line-DcBdQit6.js +1 -0
- package/dist/ui/assets/linear-HKjRHFAO.js +1 -0
- package/dist/ui/assets/mermaid-parser.core-DkYXrPlA.js +4 -0
- package/dist/ui/assets/mermaid.core-BmkfCI3b.js +9 -0
- package/dist/ui/assets/mindmap-definition-RKZ34NQL-sIAd4nDi.js +96 -0
- package/dist/ui/assets/ordinal-hYBb2elL.js +1 -0
- package/dist/ui/assets/otacon-DPXGiaVj.svg +11 -0
- package/dist/ui/assets/packet-YPE3B663-BxbxcfXN.js +1 -0
- package/dist/ui/assets/path-BWPyau1x.js +1 -0
- package/dist/ui/assets/pie-LRSECV5Y-BJxazjNs.js +1 -0
- package/dist/ui/assets/pieDiagram-4H26LBE5-BiOhc9GR.js +30 -0
- package/dist/ui/assets/plan-view-CH6NzUDb.js +74 -0
- package/dist/ui/assets/purify.es-CDvCXckx.js +3 -0
- package/dist/ui/assets/quadrantDiagram-W4KKPZXB-CVyHbWgo.js +7 -0
- package/dist/ui/assets/radar-GUYGQ44K-D9ohbnbV.js +1 -0
- package/dist/ui/assets/requirementDiagram-4Y6WPE33-Ba24_hqc.js +84 -0
- package/dist/ui/assets/rough.esm-CSKSodPl.js +1 -0
- package/dist/ui/assets/sankeyDiagram-5OEKKPKP-CxD4wiPL.js +40 -0
- package/dist/ui/assets/sequenceDiagram-3UESZ5HK-7qA7lD61.js +162 -0
- package/dist/ui/assets/src-IM8AE8MK.js +1 -0
- package/dist/ui/assets/stateDiagram-AJRCARHV-DNElRCuH.js +1 -0
- package/dist/ui/assets/stateDiagram-v2-BHNVJYJU-D6qTYpY3.js +1 -0
- package/dist/ui/assets/timeline-definition-PNZ67QCA-ChYC4Grd.js +120 -0
- package/dist/ui/assets/treeView-BLDUP644-Il0KnMi_.js +1 -0
- package/dist/ui/assets/treemap-LRROVOQU-CIiKcdRo.js +1 -0
- package/dist/ui/assets/vennDiagram-CIIHVFJN-Ulhkum9i.js +34 -0
- package/dist/ui/assets/wardley-L42UT6IY-BNd4ljz7.js +1 -0
- package/dist/ui/assets/wardleyDiagram-YWT4CUSO-BicXxh84.js +78 -0
- package/dist/ui/assets/xychartDiagram-2RQKCTM6-Duf-m_th.js +7 -0
- package/dist/ui/index.html +20 -0
- package/package.json +66 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { CITATION_RE, SESSION_STATUSES } from "../../shared/types.js";
|
|
2
|
+
// Rule semantics follow DESIGN.md §4-5; resolved edge cases follow
|
|
3
|
+
// DECISIONS.md ("Schema is closed", "Frontmatter authority", "Plan grammar").
|
|
4
|
+
const REQUIRED_FRONTMATTER_KEYS = [
|
|
5
|
+
"title",
|
|
6
|
+
"session",
|
|
7
|
+
"revision",
|
|
8
|
+
"status",
|
|
9
|
+
"created",
|
|
10
|
+
];
|
|
11
|
+
/**
|
|
12
|
+
* The H2 sections in canonical order. Optional sections (Contract, Impact) are
|
|
13
|
+
* linted when present but never required (DESIGN.md §4, q3): a trivial plan
|
|
14
|
+
* stays minimal, a complex one scales up. The order check tolerates absent
|
|
15
|
+
* optionals — it compares the sections found against this order filtered to the
|
|
16
|
+
* ones actually present, so dropping an optional never trips E_SECTION_ORDER.
|
|
17
|
+
*/
|
|
18
|
+
const ORDERED_SECTIONS = [
|
|
19
|
+
{ id: "summary", title: "Summary" },
|
|
20
|
+
{ id: "contract", title: "Contract", optional: true },
|
|
21
|
+
{ id: "decisions", title: "Decisions" },
|
|
22
|
+
{ id: "impact", title: "Impact", optional: true },
|
|
23
|
+
{ id: "phases", title: "Phases" },
|
|
24
|
+
{ id: "risks", title: "Risks" },
|
|
25
|
+
{ id: "open-questions", title: "Open Questions" },
|
|
26
|
+
];
|
|
27
|
+
/** All sections the schema accepts, for the unknown-section message. */
|
|
28
|
+
const KNOWN_SECTION_TITLES = ORDERED_SECTIONS.map((s) => s.title).join(", ");
|
|
29
|
+
const REQUIRED_PHASE_FIELDS = [
|
|
30
|
+
{ name: "goal", label: "Goal" },
|
|
31
|
+
{ name: "files", label: "Files" },
|
|
32
|
+
{ name: "verification", label: "Verification" },
|
|
33
|
+
];
|
|
34
|
+
function issue(rule, code, severity, message, extra = {}) {
|
|
35
|
+
return { rule, code, severity, message, ...extra };
|
|
36
|
+
}
|
|
37
|
+
function phaseSlug(phase) {
|
|
38
|
+
return phase.headingValid ? `phase-${phase.n}` : "phases";
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The shape verdicts shared by every gwt block, wherever it sits: empty (no
|
|
42
|
+
* scenario) and malformed (a scenario that doesn't read Given… When… Then…).
|
|
43
|
+
* `where` prefixes the message ("Phase 2", "Section ## Summary"); `slug` anchors
|
|
44
|
+
* the issue. Scenarios are pre-tokenized at parse time, so this never re-parses.
|
|
45
|
+
*/
|
|
46
|
+
function checkGwtShape(issues, gwt, where, slug) {
|
|
47
|
+
if (gwt.scenarios.length === 0) {
|
|
48
|
+
issues.push(issue("L1", "E_GWT_EMPTY", "error", `${where} has an empty \`gwt\` block — add at least one Given/When/Then scenario`, { line: gwt.startLine, section: slug }));
|
|
49
|
+
}
|
|
50
|
+
gwt.scenarios.forEach((scenario, i) => {
|
|
51
|
+
if (!scenario.valid) {
|
|
52
|
+
issues.push(issue("L1", "E_GWT_MALFORMED", "error", `${where} \`gwt\` scenario ${i + 1} must read Given… When… Then… (in order)`, { line: gwt.startLine, section: slug }));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function checkL1(plan, session) {
|
|
57
|
+
const issues = [];
|
|
58
|
+
if (plan.frontmatter === null) {
|
|
59
|
+
issues.push(issue("L1", "E_FRONTMATTER_MISSING", "error", "Plan has no frontmatter block", {
|
|
60
|
+
line: 1,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
for (const key of REQUIRED_FRONTMATTER_KEYS) {
|
|
65
|
+
if (!plan.frontmatter[key]) {
|
|
66
|
+
issues.push(issue("L1", "E_FRONTMATTER_KEY", "error", `Frontmatter is missing "${key}"`, {
|
|
67
|
+
line: 1,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (session && plan.frontmatter.session && plan.frontmatter.session !== session) {
|
|
72
|
+
issues.push(issue("L1", "E_SESSION_MISMATCH", "error", `Frontmatter session "${plan.frontmatter.session}" does not match target session "${session}"`, { line: 1 }));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const knownIds = ORDERED_SECTIONS.map((s) => s.id);
|
|
76
|
+
const seen = new Set();
|
|
77
|
+
const firstOccurrences = [];
|
|
78
|
+
for (const section of plan.sections) {
|
|
79
|
+
if (!knownIds.includes(section.id)) {
|
|
80
|
+
issues.push(issue("L1", "E_UNKNOWN_SECTION", "error", `Unknown section "## ${section.title}" — the schema allows only ${KNOWN_SECTION_TITLES}`, { line: section.startLine, section: section.id }));
|
|
81
|
+
}
|
|
82
|
+
else if (seen.has(section.id)) {
|
|
83
|
+
issues.push(issue("L1", "E_DUPLICATE_SECTION", "error", `Section "## ${section.title}" appears more than once`, { line: section.startLine, section: section.id }));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
seen.add(section.id);
|
|
87
|
+
firstOccurrences.push(section.id);
|
|
88
|
+
}
|
|
89
|
+
// A gwt fence in a section's read path (outside any phase) is misplaced —
|
|
90
|
+
// behavioral assertions live under a phase's Verification. Reject it here so
|
|
91
|
+
// it can't slip through as an ordinary budgeted fence while the UI still
|
|
92
|
+
// renders it as a scenario checklist (DESIGN.md §4).
|
|
93
|
+
for (const gwt of section.gwtBlocks) {
|
|
94
|
+
issues.push(issue("L1", "E_GWT_PLACEMENT", "error", `Section "## ${section.title}" has a \`gwt\` block — behavioral assertions belong under a phase's "Verification"`, { line: gwt.startLine, section: section.id }));
|
|
95
|
+
checkGwtShape(issues, gwt, `Section "## ${section.title}"`, section.id);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const { id, title, optional } of ORDERED_SECTIONS) {
|
|
99
|
+
if (!optional && !seen.has(id)) {
|
|
100
|
+
issues.push(issue("L1", "E_SECTION_MISSING", "error", `Required section "## ${title}" is missing`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Filtering the canonical order to present sections is what makes the order
|
|
104
|
+
// check tolerant of absent optionals (DESIGN.md §4): a plan without Contract
|
|
105
|
+
// simply never contributes a "contract" slot to compare against.
|
|
106
|
+
const expectedOrder = knownIds.filter((id) => seen.has(id));
|
|
107
|
+
if (firstOccurrences.join(",") !== expectedOrder.join(",")) {
|
|
108
|
+
issues.push(issue("L1", "E_SECTION_ORDER", "error", `Sections are out of order: found ${firstOccurrences.join(", ")}; expected ${expectedOrder.join(", ")}`));
|
|
109
|
+
}
|
|
110
|
+
const phasesSection = plan.sections.find((s) => s.id === "phases");
|
|
111
|
+
if (phasesSection) {
|
|
112
|
+
const phases = phasesSection.phases ?? [];
|
|
113
|
+
if (phases.length === 0) {
|
|
114
|
+
issues.push(issue("L1", "E_PHASES_EMPTY", "error", "The Phases section has no phases", {
|
|
115
|
+
line: phasesSection.startLine,
|
|
116
|
+
section: "phases",
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
const validNumbers = [];
|
|
120
|
+
for (const phase of phases) {
|
|
121
|
+
if (!phase.headingValid) {
|
|
122
|
+
issues.push(issue("L1", "E_PHASE_HEADING", "error", `Phase heading "${phase.rawHeading}" must match "### Phase <n> — <name>"`, { line: phase.startLine, section: "phases" }));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
validNumbers.push({ n: phase.n, line: phase.startLine });
|
|
126
|
+
}
|
|
127
|
+
for (const { name, label } of REQUIRED_PHASE_FIELDS) {
|
|
128
|
+
if (!phase.fields[name]) {
|
|
129
|
+
issues.push(issue("L1", "E_PHASE_FIELD_MISSING", "error", `Phase ${phase.headingValid ? phase.n : `at line ${phase.startLine}`} is missing "${label}"`, { line: phase.startLine, section: phaseSlug(phase) }));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (phase.fields.files && phase.fields.files.listItemCount === 0) {
|
|
133
|
+
issues.push(issue("L1", "E_FILES_EMPTY", "error", `Phase ${phase.n} "Files" has no list items`, {
|
|
134
|
+
line: phase.fields.files.startLine,
|
|
135
|
+
section: phaseSlug(phase),
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
if (phase.detailsCount > 1) {
|
|
139
|
+
issues.push(issue("L1", "E_DETAILS_MULTIPLE", "error", `Phase ${phase.n} has ${phase.detailsCount} "#### Details" blocks; at most one is allowed`, { line: phase.startLine, section: phaseSlug(phase) }));
|
|
140
|
+
}
|
|
141
|
+
for (const line of phase.strayH4s) {
|
|
142
|
+
issues.push(issue("L1", "E_UNEXPECTED_H4", "error", `Unexpected H4 inside phase ${phase.headingValid ? phase.n : ""} — only "#### Details" is allowed`, { line, section: phaseSlug(phase) }));
|
|
143
|
+
}
|
|
144
|
+
for (const gwt of phase.gwtBlocks) {
|
|
145
|
+
// gwt belongs under Verification; any other phase field is misplaced.
|
|
146
|
+
if (gwt.field !== "verification") {
|
|
147
|
+
issues.push(issue("L1", "E_GWT_PLACEMENT", "error", `Phase ${phase.n} has a \`gwt\` block outside Verification — behavioral assertions belong under "Verification"`, { line: gwt.startLine, section: phaseSlug(phase) }));
|
|
148
|
+
}
|
|
149
|
+
checkGwtShape(issues, gwt, `Phase ${phase.n}`, phaseSlug(phase));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const misnumbered = validNumbers.find(({ n }, i) => n !== i + 1);
|
|
153
|
+
if (misnumbered) {
|
|
154
|
+
issues.push(issue("L1", "E_PHASE_NUMBERING", "error", `Phase numbers must run 1..N in order; found phase ${misnumbered.n} out of sequence`, { line: misnumbered.line, section: "phases" }));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return issues;
|
|
158
|
+
}
|
|
159
|
+
export function checkL2(plan, budgets) {
|
|
160
|
+
const issues = [];
|
|
161
|
+
const over = (code, message, actual, budget, extra) => {
|
|
162
|
+
if (actual > budget) {
|
|
163
|
+
issues.push(issue("L2", code, "error", `${message} (${actual} > budget ${budget})`, {
|
|
164
|
+
...extra,
|
|
165
|
+
actual,
|
|
166
|
+
budget,
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const byId = new Map(plan.sections.map((s) => [s.id, s]));
|
|
171
|
+
const summary = byId.get("summary");
|
|
172
|
+
if (summary) {
|
|
173
|
+
over("E_BUDGET_SUMMARY", "Summary is over budget", summary.budgetedLineCount, budgets.summaryLines, {
|
|
174
|
+
line: summary.startLine,
|
|
175
|
+
section: "summary",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const contract = byId.get("contract");
|
|
179
|
+
if (contract) {
|
|
180
|
+
over("E_BUDGET_CONTRACT", "Contract is over budget", contract.budgetedLineCount, budgets.contractLines, {
|
|
181
|
+
line: contract.startLine,
|
|
182
|
+
section: "contract",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const impact = byId.get("impact");
|
|
186
|
+
if (impact) {
|
|
187
|
+
over("E_BUDGET_IMPACT", "Impact is over budget", impact.budgetedLineCount, budgets.impactLines, {
|
|
188
|
+
line: impact.startLine,
|
|
189
|
+
section: "impact",
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
for (const item of byId.get("decisions")?.listItems ?? []) {
|
|
193
|
+
over("E_BUDGET_DECISION", "Decision entry is over budget", item.lineCount, budgets.decisionEntryLines, {
|
|
194
|
+
line: item.startLine,
|
|
195
|
+
section: "decisions",
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const risks = byId.get("risks");
|
|
199
|
+
if (risks) {
|
|
200
|
+
over("E_BUDGET_RISKS_COUNT", "Too many risks", risks.listItems.length, budgets.risksMaxItems, {
|
|
201
|
+
line: risks.startLine,
|
|
202
|
+
section: "risks",
|
|
203
|
+
});
|
|
204
|
+
for (const item of risks.listItems) {
|
|
205
|
+
over("E_BUDGET_RISK_ENTRY", "Risk entry is over budget", item.lineCount, budgets.riskEntryLines, {
|
|
206
|
+
line: item.startLine,
|
|
207
|
+
section: "risks",
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
for (const id of ["summary", "contract", "decisions", "impact", "risks", "open-questions"]) {
|
|
212
|
+
const section = byId.get(id);
|
|
213
|
+
if (section) {
|
|
214
|
+
over("E_FENCE_CAP", `Section "## ${section.title}" has too many fenced blocks`, section.fenceCount, budgets.maxFencesPerReadSection, { line: section.startLine, section: id });
|
|
215
|
+
over("E_VISUAL_CAP", `Section "## ${section.title}" has too many visuals`, section.visualCount, budgets.maxVisualsPerReadSection, { line: section.startLine, section: id });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (const phase of byId.get("phases")?.phases ?? []) {
|
|
219
|
+
if (phase.fields.goal) {
|
|
220
|
+
over("E_BUDGET_GOAL", `Phase ${phase.n} "Goal" is over budget`, phase.fields.goal.budgetedLineCount, budgets.phaseGoalLines, {
|
|
221
|
+
line: phase.fields.goal.startLine,
|
|
222
|
+
section: phaseSlug(phase),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (phase.fields.verification) {
|
|
226
|
+
over("E_BUDGET_VERIFICATION", `Phase ${phase.n} "Verification" is over budget`, phase.fields.verification.budgetedLineCount, budgets.phaseVerificationLines, { line: phase.fields.verification.startLine, section: phaseSlug(phase) });
|
|
227
|
+
}
|
|
228
|
+
over("E_FENCE_CAP", `Phase ${phase.n} has too many fenced blocks outside Details`, phase.fenceCount, budgets.maxFencesPerReadSection, { line: phase.startLine, section: phaseSlug(phase) });
|
|
229
|
+
over("E_VISUAL_CAP", `Phase ${phase.n} has too many visuals outside Details`, phase.visualCount, budgets.maxVisualsPerReadSection, { line: phase.startLine, section: phaseSlug(phase) });
|
|
230
|
+
for (const gwt of phase.gwtBlocks) {
|
|
231
|
+
over("E_BUDGET_GWT", `Phase ${phase.n} \`gwt\` block has too many scenarios`, gwt.scenarios.length, budgets.gwtMaxScenarios, { line: gwt.startLine, section: phaseSlug(phase) });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return issues;
|
|
235
|
+
}
|
|
236
|
+
export function checkL6(plan, budgets) {
|
|
237
|
+
const issues = [];
|
|
238
|
+
for (const phase of plan.sections.find((s) => s.id === "phases")?.phases ?? []) {
|
|
239
|
+
if (phase.details && phase.details.lineCount > budgets.detailsSoftCapLines) {
|
|
240
|
+
issues.push(issue("L6", "W_DETAILS_SOFT_CAP", "warning", `Phase ${phase.n} Details is ${phase.details.lineCount} lines (soft cap ${budgets.detailsSoftCapLines})`, {
|
|
241
|
+
line: phase.details.startLine,
|
|
242
|
+
section: phaseSlug(phase),
|
|
243
|
+
actual: phase.details.lineCount,
|
|
244
|
+
budget: budgets.detailsSoftCapLines,
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return issues;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* L7 (DESIGN.md §4, §5, q6): a lead diagram near the top is *strongly
|
|
252
|
+
* recommended, not required* — ~90% of plans — so the reviewer sees the
|
|
253
|
+
* change's shape before its prose. A Summary with no ` ```mermaid ` diagram and
|
|
254
|
+
* no `<!-- no-lead-diagram -->` escape-hatch marker earns one **warning**, never
|
|
255
|
+
* an error: the linter checks presence, never usefulness (a diagram that merely
|
|
256
|
+
* restates the summary would add reading load), so it must not block a submit.
|
|
257
|
+
* A missing Summary is L1's business, not L7's — no double-report.
|
|
258
|
+
*/
|
|
259
|
+
export function checkL7(plan) {
|
|
260
|
+
const summary = plan.sections.find((s) => s.id === "summary");
|
|
261
|
+
if (!summary || summary.diagramCount > 0 || summary.leadDiagramOptOut)
|
|
262
|
+
return [];
|
|
263
|
+
return [
|
|
264
|
+
issue("L7", "W_LEAD_DIAGRAM_MISSING", "warning", "Summary has no lead diagram — a state/sequence/flow ```mermaid block up top is strongly recommended (≈90% of plans). Add one, or mark `<!-- no-lead-diagram: <why> -->` in Summary if a chart wouldn't help.", { line: summary.startLine, section: "summary" }),
|
|
265
|
+
];
|
|
266
|
+
}
|
|
267
|
+
const DECISION_RE = /^[-*+]\s+(D\d+):/;
|
|
268
|
+
// CITATION_RE (src/shared/types.ts) is shared with the UI's deep-link
|
|
269
|
+
// transform. Global matters here: an entry can carry several citation clauses
|
|
270
|
+
// ("… ← q1; revisit ← q9"), and every cited id must be checked — validating
|
|
271
|
+
// only the first would let a fabricated later clause game traceability
|
|
272
|
+
// invisibly.
|
|
273
|
+
/**
|
|
274
|
+
* L3 (DESIGN.md §4, §5, §8): every `- D<n>:` decision entry must cite the
|
|
275
|
+
* grill question(s) that produced it (`← q7`) or wear `[assumed]`, and every
|
|
276
|
+
* cited q id must exist in the transcript. Errors normally; warnings in
|
|
277
|
+
* --quick sessions (codes stay stable — severity is the contextual dimension).
|
|
278
|
+
*/
|
|
279
|
+
export function checkL3(plan, ctx) {
|
|
280
|
+
const severity = ctx.quick ? "warning" : "error";
|
|
281
|
+
const known = new Set(ctx.knownQuestions);
|
|
282
|
+
const issues = [];
|
|
283
|
+
const decisions = plan.sections.find((s) => s.id === "decisions");
|
|
284
|
+
for (const item of decisions?.listItems ?? []) {
|
|
285
|
+
const label = DECISION_RE.exec(item.text)?.[1];
|
|
286
|
+
if (label === undefined)
|
|
287
|
+
continue; // non-D entries are not L3's business
|
|
288
|
+
const cited = [...item.text.matchAll(CITATION_RE)].flatMap((m) => m[1].split(",").map((q) => q.trim()));
|
|
289
|
+
if (cited.length === 0 && !item.text.includes("[assumed]")) {
|
|
290
|
+
issues.push(issue("L3", "E_DECISION_UNTRACED", severity, `Decision ${label} cites no grill question — add "← q<n>" or tag it [assumed]`, { line: item.startLine, section: "decisions" }));
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
for (const qid of cited) {
|
|
294
|
+
if (!known.has(qid)) {
|
|
295
|
+
issues.push(issue("L3", "E_UNKNOWN_QUESTION_CITED", severity, `Decision ${label} cites ${qid}, which is not in this session's grill transcript`, { line: item.startLine, section: "decisions" }));
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return issues;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* L5 (DESIGN.md §5, §9): a resubmit must resolve every unresolved comment
|
|
303
|
+
* thread, and every revision ≥ 2 must carry a changelog. Unknown thread ids
|
|
304
|
+
* (typos, question ids — questions are answered via `otacon answer`, never
|
|
305
|
+
* resolved) are errors; re-resolving an already-resolved thread is allowed
|
|
306
|
+
* because at-least-once delivery makes duplicate submits legitimate.
|
|
307
|
+
*/
|
|
308
|
+
export function checkL5(ctx) {
|
|
309
|
+
const issues = [];
|
|
310
|
+
const known = new Set(ctx.commentThreads.map((t) => t.id));
|
|
311
|
+
for (const [thread, reply] of Object.entries(ctx.replies)) {
|
|
312
|
+
if (!known.has(thread)) {
|
|
313
|
+
issues.push(issue("L5", "E_UNKNOWN_THREAD", "error", `Resolution targets unknown comment thread "${thread}"`, { thread }));
|
|
314
|
+
}
|
|
315
|
+
else if (reply.trim() === "") {
|
|
316
|
+
issues.push(issue("L5", "E_EMPTY_RESOLUTION", "error", `Resolution reply for thread "${thread}" is empty`, { thread }));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
for (const { id, resolved } of ctx.commentThreads) {
|
|
320
|
+
if (resolved)
|
|
321
|
+
continue;
|
|
322
|
+
const reply = ctx.replies[id];
|
|
323
|
+
if (reply === undefined) {
|
|
324
|
+
issues.push(issue("L5", "E_THREAD_UNRESOLVED", "error", `Comment thread "${id}" has no resolution reply — every open thread needs one (submit --resolutions)`, { thread: id }));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (ctx.revision >= 2 && (ctx.changelog ?? "").trim() === "") {
|
|
328
|
+
issues.push(issue("L5", "E_CHANGELOG_MISSING", "error", `Revision ${ctx.revision} needs a changelog summarizing what changed (resolutions.json "changelog")`));
|
|
329
|
+
}
|
|
330
|
+
return issues;
|
|
331
|
+
}
|
|
332
|
+
/** A3 warnings: the daemon owns revision/status, so value drift only warns. */
|
|
333
|
+
export function checkFrontmatterAuthority(plan, expectations = {}) {
|
|
334
|
+
const issues = [];
|
|
335
|
+
const fm = plan.frontmatter;
|
|
336
|
+
if (!fm)
|
|
337
|
+
return issues;
|
|
338
|
+
if (fm.revision !== undefined) {
|
|
339
|
+
const revision = Number(fm.revision);
|
|
340
|
+
if (!Number.isInteger(revision) || revision < 1) {
|
|
341
|
+
issues.push(issue("L1", "W_REVISION_INVALID", "warning", `Frontmatter revision "${fm.revision}" is not a positive integer`, { line: 1 }));
|
|
342
|
+
}
|
|
343
|
+
else if (expectations.expectedRevision !== undefined &&
|
|
344
|
+
revision !== expectations.expectedRevision) {
|
|
345
|
+
issues.push(issue("L1", "W_REVISION_MISMATCH", "warning", `Frontmatter revision ${revision} differs from the daemon's ${expectations.expectedRevision} (daemon wins)`, { line: 1 }));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (fm.status !== undefined) {
|
|
349
|
+
if (!SESSION_STATUSES.includes(fm.status)) {
|
|
350
|
+
issues.push(issue("L1", "W_STATUS_INVALID", "warning", `Frontmatter status "${fm.status}" is not one of ${SESSION_STATUSES.join("/")}`, { line: 1 }));
|
|
351
|
+
}
|
|
352
|
+
else if (expectations.expectedStatus !== undefined &&
|
|
353
|
+
fm.status !== expectations.expectedStatus) {
|
|
354
|
+
issues.push(issue("L1", "W_STATUS_UNEXPECTED", "warning", `Frontmatter status "${fm.status}" differs from the daemon's "${expectations.expectedStatus}" (daemon wins)`, { line: 1 }));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return issues;
|
|
358
|
+
}
|
|
359
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../src/daemon/linter/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// otacond entry point: bind the Hono app to 127.0.0.1 only (DESIGN.md §3;
|
|
3
|
+
// remote access is Tailscale's job, never a wider bind).
|
|
4
|
+
import { serve } from "@hono/node-server";
|
|
5
|
+
import { otaconPort } from "../shared/paths.js";
|
|
6
|
+
import { VERSION } from "../shared/version.js";
|
|
7
|
+
import { createApp } from "./app.js";
|
|
8
|
+
import { Store } from "./store.js";
|
|
9
|
+
const HOST = "127.0.0.1";
|
|
10
|
+
const port = otaconPort();
|
|
11
|
+
const app = createApp({
|
|
12
|
+
store: new Store(),
|
|
13
|
+
// The shutdown route invokes this only after its response is written
|
|
14
|
+
// (or the client is gone), so exiting immediately is safe.
|
|
15
|
+
onShutdown: () => process.exit(0),
|
|
16
|
+
});
|
|
17
|
+
const server = serve({ fetch: app.fetch, hostname: HOST, port }, (info) => {
|
|
18
|
+
process.stdout.write(`${JSON.stringify({ app: "otacond", version: VERSION, host: HOST, port: info.port, pid: process.pid })}\n`);
|
|
19
|
+
});
|
|
20
|
+
server.on("error", (error) => {
|
|
21
|
+
if (error.code === "EADDRINUSE") {
|
|
22
|
+
// The port is the lock (DECISIONS.md "Spawn race"): losing the bind to
|
|
23
|
+
// another otacond is success; squatting by anything else refuses to start.
|
|
24
|
+
void exitPerPortOwner();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
process.stderr.write(`otacond: ${error.message}\n`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
async function exitPerPortOwner() {
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(`http://${HOST}:${port}/api/health`, {
|
|
34
|
+
signal: AbortSignal.timeout(2000),
|
|
35
|
+
});
|
|
36
|
+
const health = (await response.json());
|
|
37
|
+
if (health.app === "otacond") {
|
|
38
|
+
process.stdout.write(`${JSON.stringify({ app: "otacond", note: "already running", version: health.version, host: HOST, port })}\n`);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Not an otacond (or not even HTTP) — fall through to the refusal.
|
|
44
|
+
}
|
|
45
|
+
process.stderr.write(`otacond: port ${port} is in use by something that is not otacond; set OTACON_PORT to pick another port\n`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/daemon/main.ts"],"names":[],"mappings":";AACA,0EAA0E;AAC1E,yDAAyD;AAEzD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,IAAI,GAAG,WAAW,CAAC;AACzB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;AAE1B,MAAM,GAAG,GAAG,SAAS,CAAC;IACpB,KAAK,EAAE,IAAI,KAAK,EAAE;IAClB,qEAAqE;IACrE,2DAA2D;IAC3D,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;CAClC,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;IACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAC3G,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;IAClD,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAChC,uEAAuE;QACvE,2EAA2E;QAC3E,KAAK,gBAAgB,EAAE,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,gBAAgB;IAC7B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,UAAU,IAAI,IAAI,IAAI,aAAa,EAAE;YAChE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuC,CAAC;QAC7E,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,CAC9G,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,mEAAmE;IACrE,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iBAAiB,IAAI,qFAAqF,CAC3G,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// In-process pub/sub bridging daemon state changes to the web UI's SSE
|
|
2
|
+
// streams (DESIGN.md §10 "UI updates over SSE"). One daemon process owns all
|
|
3
|
+
// state, so an EventEmitter is the entire bus (DECISIONS.md "UI live updates:
|
|
4
|
+
// in-process Notifier, snapshot-first SSE, no replay").
|
|
5
|
+
import { EventEmitter } from "node:events";
|
|
6
|
+
export class Notifier {
|
|
7
|
+
emitter = new EventEmitter();
|
|
8
|
+
constructor() {
|
|
9
|
+
// One listener per open SSE stream; the default 10-listener warning is noise.
|
|
10
|
+
this.emitter.setMaxListeners(0);
|
|
11
|
+
}
|
|
12
|
+
publish(event) {
|
|
13
|
+
this.emitter.emit("event", event);
|
|
14
|
+
}
|
|
15
|
+
/** Returns the unsubscribe function. */
|
|
16
|
+
subscribe(listener) {
|
|
17
|
+
this.emitter.on("event", listener);
|
|
18
|
+
return () => {
|
|
19
|
+
this.emitter.off("event", listener);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/daemon/notify.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,6EAA6E;AAC7E,8EAA8E;AAC9E,wDAAwD;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA4B3C,MAAM,OAAO,QAAQ;IACF,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;IAE9C;QACE,8EAA8E;QAC9E,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,KAAc;QACpB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,wCAAwC;IACxC,SAAS,CAAC,QAAkC;QAC1C,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACnC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Tracks which sessions have a *visible* review open, so a desktop banner is
|
|
2
|
+
// suppressed only while the user is actually looking (DESIGN.md §6, §10). The
|
|
3
|
+
// UI reports `document.visibilityState` over POST /api/sessions/:id/presence —
|
|
4
|
+
// {visible:true} when the review becomes visible and on a heartbeat while it
|
|
5
|
+
// stays visible, {visible:false} on visibilitychange→hidden and on unload.
|
|
6
|
+
//
|
|
7
|
+
// Suppression keys on visibility, NOT a live SSE connection: a hidden or
|
|
8
|
+
// backgrounded tab keeps its stream open, so a connection count would wrongly
|
|
9
|
+
// silence banners when the user can't see the page. A TTL makes a crashed or
|
|
10
|
+
// closed visible tab self-expire (it stops sending heartbeats), while an
|
|
11
|
+
// explicit hidden ping un-suppresses immediately.
|
|
12
|
+
/** A visible tab is "watched" for this long after its last heartbeat, then expires. */
|
|
13
|
+
const WATCH_TTL_MS = 45_000;
|
|
14
|
+
export class Presence {
|
|
15
|
+
now;
|
|
16
|
+
ttlMs;
|
|
17
|
+
lastVisibleAt = new Map();
|
|
18
|
+
/** `now`/`ttlMs` are injectable so tests drive expiry without real time. */
|
|
19
|
+
constructor(now = Date.now, ttlMs = WATCH_TTL_MS) {
|
|
20
|
+
this.now = now;
|
|
21
|
+
this.ttlMs = ttlMs;
|
|
22
|
+
}
|
|
23
|
+
/** The session's review just became (or stays) visible — refresh its heartbeat. */
|
|
24
|
+
markVisible(id) {
|
|
25
|
+
this.lastVisibleAt.set(id, this.now());
|
|
26
|
+
}
|
|
27
|
+
/** An explicit "no longer visible" ping (blur/unload) — un-suppress immediately. */
|
|
28
|
+
markHidden(id) {
|
|
29
|
+
this.lastVisibleAt.delete(id);
|
|
30
|
+
}
|
|
31
|
+
/** True while a visible tab's heartbeat is fresh; a crashed/closed tab self-expires. */
|
|
32
|
+
isWatched(id) {
|
|
33
|
+
const at = this.lastVisibleAt.get(id);
|
|
34
|
+
return at !== undefined && this.now() - at < this.ttlMs;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/daemon/presence.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,+EAA+E;AAC/E,6EAA6E;AAC7E,2EAA2E;AAC3E,EAAE;AACF,yEAAyE;AACzE,8EAA8E;AAC9E,6EAA6E;AAC7E,yEAAyE;AACzE,kDAAkD;AAElD,uFAAuF;AACvF,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,MAAM,OAAO,QAAQ;IAKA;IACA;IALF,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3D,4EAA4E;IAC5E,YACmB,MAAoB,IAAI,CAAC,GAAG,EAC5B,QAAgB,YAAY;QAD5B,QAAG,GAAH,GAAG,CAAyB;QAC5B,UAAK,GAAL,KAAK,CAAuB;IAC5C,CAAC;IAEJ,mFAAmF;IACnF,WAAW,CAAC,EAAU;QACpB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,oFAAoF;IACpF,UAAU,CAAC,EAAU;QACnB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,wFAAwF;IACxF,SAAS,CAAC,EAAU;QAClB,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtC,OAAO,EAAE,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;IAC1D,CAAC;CACF"}
|