@zigrivers/scaffold 3.29.0 → 3.31.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/content/guides/AUTHORING.md +146 -0
- package/content/guides/cli/index.html +1855 -0
- package/content/guides/cli/index.md +206 -0
- package/content/guides/concepts/index.html +1970 -0
- package/content/guides/concepts/index.md +347 -0
- package/content/guides/dashboard/index.html +1913 -0
- package/content/guides/dashboard/index.md +264 -0
- package/content/guides/index.html +368 -15
- package/content/guides/install/.diagrams/diagram-0.svg +1 -0
- package/content/guides/install/.diagrams/manifest.json +3 -0
- package/content/guides/install/index.html +1653 -0
- package/content/guides/install/index.md +186 -0
- package/content/guides/knowledge/.diagrams/diagram-0.svg +1 -0
- package/content/guides/knowledge/.diagrams/manifest.json +3 -0
- package/content/guides/knowledge/index.html +1765 -0
- package/content/guides/knowledge/index.md +209 -0
- package/content/guides/knowledge-freshness/.diagrams/diagram-0.svg +1 -0
- package/content/guides/knowledge-freshness/.diagrams/manifest.json +3 -0
- package/content/guides/knowledge-freshness/index.html +2795 -0
- package/content/guides/knowledge-freshness/index.md +893 -0
- package/content/guides/mmr/index.html +407 -36
- package/content/guides/mmr/index.md +39 -16
- package/content/guides/multi-agent/.diagrams/diagram-0.svg +1 -0
- package/content/guides/multi-agent/.diagrams/manifest.json +3 -0
- package/content/guides/multi-agent/index.html +1715 -0
- package/content/guides/multi-agent/index.md +243 -0
- package/content/guides/observability/.diagrams/diagram-0.svg +1 -0
- package/content/guides/observability/.diagrams/diagram-1.svg +1 -0
- package/content/guides/observability/.diagrams/diagram-2.svg +1 -0
- package/content/guides/observability/.diagrams/diagram-3.svg +1 -0
- package/content/guides/observability/.diagrams/manifest.json +6 -0
- package/content/guides/observability/index.html +3257 -0
- package/content/guides/observability/index.md +1097 -0
- package/content/guides/pipeline/.diagrams/diagram-0.svg +1 -0
- package/content/guides/pipeline/.diagrams/diagram-1.svg +1 -0
- package/content/guides/pipeline/.diagrams/manifest.json +4 -0
- package/content/guides/pipeline/index.html +1973 -0
- package/content/guides/pipeline/index.md +387 -0
- package/content/guides/review-workflow/.diagrams/diagram-0.svg +1 -0
- package/content/guides/review-workflow/.diagrams/diagram-1.svg +1 -0
- package/content/guides/review-workflow/.diagrams/manifest.json +4 -0
- package/content/guides/review-workflow/index.html +1790 -0
- package/content/guides/review-workflow/index.md +248 -0
- package/dist/guides/build.d.ts +1 -1
- package/dist/guides/build.d.ts.map +1 -1
- package/dist/guides/build.js +21 -9
- package/dist/guides/build.js.map +1 -1
- package/dist/guides/build.test.js +47 -0
- package/dist/guides/build.test.js.map +1 -1
- package/dist/guides/chrome.d.ts.map +1 -1
- package/dist/guides/chrome.js +83 -12
- package/dist/guides/chrome.js.map +1 -1
- package/dist/guides/dashboard-theme.css +8 -0
- package/dist/guides/directives-cite.test.d.ts +2 -0
- package/dist/guides/directives-cite.test.d.ts.map +1 -0
- package/dist/guides/directives-cite.test.js +26 -0
- package/dist/guides/directives-cite.test.js.map +1 -0
- package/dist/guides/directives-tabs.test.js +47 -0
- package/dist/guides/directives-tabs.test.js.map +1 -1
- package/dist/guides/directives.d.ts +1 -0
- package/dist/guides/directives.d.ts.map +1 -1
- package/dist/guides/directives.js +38 -0
- package/dist/guides/directives.js.map +1 -1
- package/dist/guides/guides.css +268 -0
- package/dist/guides/index-page.d.ts.map +1 -1
- package/dist/guides/index-page.js +41 -8
- package/dist/guides/index-page.js.map +1 -1
- package/dist/guides/links.d.ts +14 -0
- package/dist/guides/links.d.ts.map +1 -0
- package/dist/guides/links.js +56 -0
- package/dist/guides/links.js.map +1 -0
- package/dist/guides/links.test.d.ts +2 -0
- package/dist/guides/links.test.d.ts.map +1 -0
- package/dist/guides/links.test.js +72 -0
- package/dist/guides/links.test.js.map +1 -0
- package/dist/guides/render.d.ts +1 -0
- package/dist/guides/render.d.ts.map +1 -1
- package/dist/guides/render.js +1 -1
- package/dist/guides/render.js.map +1 -1
- package/dist/guides/sanitize.d.ts.map +1 -1
- package/dist/guides/sanitize.js +5 -0
- package/dist/guides/sanitize.js.map +1 -1
- package/dist/guides/template.d.ts.map +1 -1
- package/dist/guides/template.js +7 -2
- package/dist/guides/template.js.map +1 -1
- package/package.json +2 -2
|
@@ -48,9 +48,13 @@
|
|
|
48
48
|
--yellow: #d97706;
|
|
49
49
|
--yellow-bg: #fffbeb;
|
|
50
50
|
--yellow-border:#fde68a;
|
|
51
|
+
--red: #dc2626;
|
|
52
|
+
--red-bg: #fef2f2;
|
|
53
|
+
--red-border: #fecaca;
|
|
51
54
|
--gray: #9ca3af;
|
|
52
55
|
--gray-bg: #f3f4f6;
|
|
53
56
|
--gray-border: #e5e7eb;
|
|
57
|
+
--scrim: rgba(15, 17, 23, 0.45);
|
|
54
58
|
|
|
55
59
|
/* Semantic: Next Banner */
|
|
56
60
|
--next-bg: #eef2ff;
|
|
@@ -133,9 +137,13 @@
|
|
|
133
137
|
--yellow: #fbbf24;
|
|
134
138
|
--yellow-bg: rgba(120, 53, 15, 0.25);
|
|
135
139
|
--yellow-border:rgba(251, 191, 36, 0.20);
|
|
140
|
+
--red: #f87171;
|
|
141
|
+
--red-bg: rgba(127, 29, 29, 0.25);
|
|
142
|
+
--red-border: rgba(248, 113, 113, 0.22);
|
|
136
143
|
--gray: #6b7294;
|
|
137
144
|
--gray-bg: #252940;
|
|
138
145
|
--gray-border: #363c58;
|
|
146
|
+
--scrim: rgba(0, 0, 0, 0.6);
|
|
139
147
|
|
|
140
148
|
/* Semantic: Next Banner */
|
|
141
149
|
--next-bg: rgba(30, 27, 75, 0.50);
|
|
@@ -1078,17 +1086,290 @@ figure.mermaid svg .marker {
|
|
|
1078
1086
|
}
|
|
1079
1087
|
figure.mermaid svg .edgeLabel,
|
|
1080
1088
|
figure.mermaid svg .edgeLabel text { fill: var(--text-muted); color: var(--text-muted); }
|
|
1089
|
+
|
|
1090
|
+
/* ============================================================================
|
|
1091
|
+
* guides.css — component + layout styles for `scaffold guides` reference pages.
|
|
1092
|
+
*
|
|
1093
|
+
* Pairs with lib/dashboard-theme.css (the token source) and src/guides/chrome.ts
|
|
1094
|
+
* (the behavior). Styles the guide CHROME (.topbar, .layout, .rail, nav.toc,
|
|
1095
|
+
* .content) and the markdown DIRECTIVES (callouts, sev chips, filter-tables,
|
|
1096
|
+
* charts, tabs, citations) plus base prose typography.
|
|
1097
|
+
*
|
|
1098
|
+
* DESIGN SYSTEM: all COLORS come from dashboard-theme.css tokens, and spacing
|
|
1099
|
+
* uses the --sp-* scale wherever it maps. The few structural layout constants
|
|
1100
|
+
* (topbar height, rail/drawer width, chart label column, card min) are declared
|
|
1101
|
+
* as local custom properties below; a handful of sub-scale UI values (chip/bar
|
|
1102
|
+
* sizing, em-based inline-code padding) and the responsive breakpoint are
|
|
1103
|
+
* literal because no token expresses them. Both themes are covered because every
|
|
1104
|
+
* color is a token.
|
|
1105
|
+
* ==========================================================================*/
|
|
1106
|
+
|
|
1107
|
+
:root {
|
|
1108
|
+
--topbar-h: 52px; /* sticky topbar height; rail sticky offset keys off it */
|
|
1109
|
+
--rail-w: 260px; /* desktop TOC sidebar column */
|
|
1110
|
+
--drawer-w: 280px; /* mobile off-canvas TOC drawer */
|
|
1111
|
+
--card-min: 260px; /* index card min track width */
|
|
1112
|
+
--chart-label-w: 90px; /* chart row label column min */
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/* ── Base / reset on top of the token base in dashboard-theme.css ─────────── */
|
|
1116
|
+
.content a { color: var(--accent); text-decoration: none; }
|
|
1117
|
+
.content a:hover { text-decoration: underline; }
|
|
1118
|
+
.content strong { font-weight: var(--fw-semi); }
|
|
1119
|
+
.content hr { border: 0; border-top: 1px solid var(--border-light); margin: var(--sp-6) 0; }
|
|
1120
|
+
|
|
1121
|
+
/* Consistent keyboard focus for every interactive control (a11y). */
|
|
1122
|
+
.topbar button:focus-visible,
|
|
1123
|
+
.copy-btn:focus-visible,
|
|
1124
|
+
.tab-btn:focus-visible,
|
|
1125
|
+
.filter-input:focus-visible,
|
|
1126
|
+
nav.toc a:focus-visible,
|
|
1127
|
+
.guide-card:focus-visible,
|
|
1128
|
+
.content a:focus-visible {
|
|
1129
|
+
outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/* ── Topbar ────────────────────────────────────────────────────────────────*/
|
|
1133
|
+
.topbar {
|
|
1134
|
+
position: sticky; top: 0; z-index: 60; height: var(--topbar-h);
|
|
1135
|
+
display: flex; align-items: center; gap: var(--sp-3);
|
|
1136
|
+
padding: 0 var(--page-pad);
|
|
1137
|
+
background: var(--bg-card); border-bottom: 1px solid var(--border);
|
|
1138
|
+
}
|
|
1139
|
+
.topbar h1 {
|
|
1140
|
+
flex: 1; min-width: 0; margin: 0;
|
|
1141
|
+
font-size: var(--text-lg); font-weight: var(--fw-bold);
|
|
1142
|
+
letter-spacing: var(--ls-tight);
|
|
1143
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
1144
|
+
}
|
|
1145
|
+
.topbar button {
|
|
1146
|
+
background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
|
|
1147
|
+
border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-3); font-size: var(--text-base);
|
|
1148
|
+
line-height: 1; cursor: pointer; font-family: inherit;
|
|
1149
|
+
}
|
|
1150
|
+
.topbar button:hover { background: var(--bg-hover); border-color: var(--accent); }
|
|
1151
|
+
.nav-toggle { display: none; }
|
|
1152
|
+
|
|
1153
|
+
/* ── Layout: sticky sidebar TOC + reading-width content ──────────────────── */
|
|
1154
|
+
.layout {
|
|
1155
|
+
max-width: var(--max-w); margin: 0 auto;
|
|
1156
|
+
display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr);
|
|
1157
|
+
gap: var(--sp-8); padding: 0 var(--page-pad);
|
|
1158
|
+
}
|
|
1159
|
+
.rail {
|
|
1160
|
+
position: sticky; top: var(--topbar-h); align-self: start;
|
|
1161
|
+
height: calc(100vh - var(--topbar-h)); overflow-y: auto;
|
|
1162
|
+
padding: var(--sp-5) 0; border-right: 1px solid var(--border-light);
|
|
1163
|
+
}
|
|
1164
|
+
.content { min-width: 0; padding: var(--sp-6) 0 var(--sp-10); }
|
|
1165
|
+
|
|
1166
|
+
/* Backdrop behind the mobile drawer (toggled with the rail via chrome.ts). */
|
|
1167
|
+
.rail-backdrop { display: none; }
|
|
1168
|
+
/* In-drawer close button — hidden on desktop (the rail is a static sidebar). */
|
|
1169
|
+
.rail-close { display: none; }
|
|
1170
|
+
|
|
1171
|
+
/* ── Table of contents (scrollspy marks a.active) ────────────────────────── */
|
|
1172
|
+
nav.toc ul { list-style: none; margin: 0; padding: 0; }
|
|
1173
|
+
nav.toc li { margin: 0; }
|
|
1174
|
+
nav.toc a {
|
|
1175
|
+
display: block; padding: var(--sp-1) var(--sp-3); line-height: 1.35;
|
|
1176
|
+
color: var(--text-muted); font-size: var(--text-sm);
|
|
1177
|
+
text-decoration: none; border-left: 2px solid transparent;
|
|
1178
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
1179
|
+
}
|
|
1180
|
+
nav.toc a:hover { color: var(--text); background: var(--bg-hover); }
|
|
1181
|
+
nav.toc a.active {
|
|
1182
|
+
color: var(--accent); border-left-color: var(--accent);
|
|
1183
|
+
background: var(--accent-glow); font-weight: var(--fw-medium);
|
|
1184
|
+
}
|
|
1185
|
+
nav.toc li.toc-3 a { padding-left: var(--sp-6); font-size: var(--text-xs); }
|
|
1186
|
+
|
|
1187
|
+
/* ── Prose typography ──────────────────────────────────────────────────────*/
|
|
1188
|
+
.content h2 {
|
|
1189
|
+
font-size: var(--text-xl); letter-spacing: var(--ls-tight);
|
|
1190
|
+
margin: var(--sp-8) 0 var(--sp-3); padding-bottom: var(--sp-2);
|
|
1191
|
+
border-bottom: 1px solid var(--border-light); scroll-margin-top: calc(var(--topbar-h) + var(--sp-3));
|
|
1192
|
+
}
|
|
1193
|
+
.content h3 {
|
|
1194
|
+
font-size: var(--text-lg); margin: var(--sp-5) 0 var(--sp-2);
|
|
1195
|
+
scroll-margin-top: calc(var(--topbar-h) + var(--sp-3));
|
|
1196
|
+
}
|
|
1197
|
+
.content p { margin: var(--sp-3) 0; line-height: var(--lh-relaxed); }
|
|
1198
|
+
.content ul, .content ol { margin: var(--sp-3) 0; padding-left: var(--sp-6); }
|
|
1199
|
+
.content li { margin: var(--sp-1) 0; line-height: var(--lh-relaxed); }
|
|
1200
|
+
.content blockquote {
|
|
1201
|
+
margin: var(--sp-3) 0; padding: var(--sp-1) var(--sp-4);
|
|
1202
|
+
border-left: 3px solid var(--border); color: var(--text-muted);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/* ── Inline code + code blocks (chrome.ts wraps <pre> in .code + .copy-btn) ──*/
|
|
1206
|
+
.content code {
|
|
1207
|
+
font-family: var(--font-mono); font-size: 0.85em;
|
|
1208
|
+
background: var(--bg-inset); padding: 0.12em 0.4em; border-radius: var(--radius-sm);
|
|
1209
|
+
}
|
|
1210
|
+
.content .code { position: relative; margin: var(--sp-3) 0; }
|
|
1211
|
+
.content .code pre {
|
|
1212
|
+
margin: 0; padding: var(--sp-3) var(--sp-4); overflow-x: auto;
|
|
1213
|
+
background: var(--bg-inset); border: 1px solid var(--border-light);
|
|
1214
|
+
border-radius: var(--radius-sm); font-family: var(--font-mono);
|
|
1215
|
+
font-size: var(--text-sm); line-height: var(--lh-relaxed);
|
|
1216
|
+
}
|
|
1217
|
+
.content .code pre code { background: none; padding: 0; font-size: inherit; }
|
|
1218
|
+
.copy-btn {
|
|
1219
|
+
position: absolute; top: var(--sp-1); right: var(--sp-1);
|
|
1220
|
+
background: var(--bg-card); border: 1px solid var(--border); color: var(--text-muted);
|
|
1221
|
+
border-radius: var(--radius-sm); font-size: var(--text-xs); padding: var(--sp-1) var(--sp-2);
|
|
1222
|
+
cursor: pointer; opacity: 0.85; font-family: inherit;
|
|
1223
|
+
}
|
|
1224
|
+
.copy-btn:hover { color: var(--accent); border-color: var(--accent); opacity: 1; }
|
|
1225
|
+
|
|
1226
|
+
/* ── Callouts ─ (border-color BEFORE border-left-color so the accent wins) ── */
|
|
1227
|
+
.callout {
|
|
1228
|
+
margin: var(--sp-4) 0; padding: var(--sp-3) var(--sp-4);
|
|
1229
|
+
border: 1px solid var(--border); border-left-width: 3px;
|
|
1230
|
+
border-radius: var(--radius-sm); background: var(--bg-card);
|
|
1231
|
+
}
|
|
1232
|
+
.callout > :first-child { margin-top: 0; }
|
|
1233
|
+
.callout > :last-child { margin-bottom: 0; }
|
|
1234
|
+
.callout-note, .callout-info { background: var(--blue-bg); border-color: var(--blue-border); border-left-color: var(--blue); }
|
|
1235
|
+
.callout-tip { background: var(--green-bg); border-color: var(--green-border); border-left-color: var(--green); }
|
|
1236
|
+
.callout-warning { background: var(--yellow-bg); border-color: var(--yellow-border); border-left-color: var(--yellow); }
|
|
1237
|
+
.callout-danger { background: var(--red-bg); border-color: var(--red-border); border-left-color: var(--red); }
|
|
1238
|
+
|
|
1239
|
+
/* ── Severity chips (:sev) — tight pill, sub-scale vertical padding ───────── */
|
|
1240
|
+
.sev {
|
|
1241
|
+
display: inline-block; font-size: var(--text-xs); font-weight: var(--fw-semi);
|
|
1242
|
+
padding: 1px var(--sp-2); border-radius: 999px; line-height: 1.5;
|
|
1243
|
+
border: 1px solid var(--border); background: var(--bg-inset); color: var(--text-muted);
|
|
1244
|
+
white-space: nowrap;
|
|
1245
|
+
}
|
|
1246
|
+
.sev-p0 { color: var(--sev-p0); border-color: var(--sev-p0); }
|
|
1247
|
+
.sev-p1 { color: var(--sev-p1); border-color: var(--sev-p1); }
|
|
1248
|
+
.sev-p2 { color: var(--sev-p2); border-color: var(--sev-p2); }
|
|
1249
|
+
.sev-p3 { color: var(--sev-p3); border-color: var(--sev-p3); }
|
|
1250
|
+
.sev-pass { color: var(--sev-pass); border-color: var(--sev-pass); }
|
|
1251
|
+
|
|
1252
|
+
/* ── Citations (:cite) — inline provenance refs ──────────────────────────── */
|
|
1253
|
+
.fp, .cite-advisory {
|
|
1254
|
+
font-family: var(--font-mono); font-size: 0.82em;
|
|
1255
|
+
padding: 0.05em 0.35em; border-radius: var(--radius-sm);
|
|
1256
|
+
background: var(--bg-inset); border: 1px solid var(--border-light);
|
|
1257
|
+
}
|
|
1258
|
+
.fp { color: var(--accent); }
|
|
1259
|
+
.cite-advisory { color: var(--text-faint); border-style: dashed; }
|
|
1260
|
+
|
|
1261
|
+
/* ── Tables + filter-tables ──────────────────────────────────────────────── */
|
|
1262
|
+
.content table { width: 100%; border-collapse: collapse; margin: var(--sp-3) 0; font-size: var(--text-sm); }
|
|
1263
|
+
.content th, .content td { text-align: left; padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border-light); vertical-align: top; }
|
|
1264
|
+
.content th {
|
|
1265
|
+
color: var(--text-muted); font-weight: var(--fw-semi); font-size: var(--text-xs);
|
|
1266
|
+
text-transform: uppercase; letter-spacing: var(--ls-wide); border-bottom-color: var(--border);
|
|
1267
|
+
}
|
|
1268
|
+
.content tbody tr:hover { background: var(--bg-hover); }
|
|
1269
|
+
.content td code { white-space: nowrap; }
|
|
1270
|
+
.filter-table { margin: var(--sp-4) 0; }
|
|
1271
|
+
.filter-input {
|
|
1272
|
+
width: 100%; max-width: 320px; margin-bottom: var(--sp-2);
|
|
1273
|
+
padding: var(--sp-2) var(--sp-3); font-family: inherit; font-size: var(--text-sm);
|
|
1274
|
+
color: var(--text); background: var(--bg-card);
|
|
1275
|
+
border: 1px solid var(--border); border-radius: var(--radius-sm);
|
|
1276
|
+
}
|
|
1277
|
+
.filter-input:focus { border-color: var(--accent); }
|
|
1278
|
+
|
|
1279
|
+
/* ── Charts (:::chart) — label + proportional bar (fill carries inline width%) */
|
|
1280
|
+
.chart-block { margin: var(--sp-4) 0; }
|
|
1281
|
+
.chart-row {
|
|
1282
|
+
display: grid; grid-template-columns: minmax(var(--chart-label-w), 24%) 1fr;
|
|
1283
|
+
align-items: center; gap: var(--sp-3); margin: var(--sp-1) 0;
|
|
1284
|
+
}
|
|
1285
|
+
.chart-label {
|
|
1286
|
+
font-size: var(--text-sm); color: var(--text-muted); text-align: right;
|
|
1287
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
1288
|
+
}
|
|
1289
|
+
.chart-row .chart-bar {
|
|
1290
|
+
height: 0.9em; min-width: 2px; background: var(--accent);
|
|
1291
|
+
border-radius: var(--radius-sm);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/* ── Tabs (::::tabs / :::tab) ─────────────────────────────────────────────── */
|
|
1295
|
+
.tabs { margin: var(--sp-4) 0; }
|
|
1296
|
+
.tablist { display: flex; flex-wrap: wrap; gap: var(--sp-1); border-bottom: 1px solid var(--border); margin-bottom: var(--sp-3); }
|
|
1297
|
+
.tab-btn {
|
|
1298
|
+
background: none; border: none; border-bottom: 2px solid transparent; margin-bottom: -1px;
|
|
1299
|
+
padding: var(--sp-2) var(--sp-3); color: var(--text-muted);
|
|
1300
|
+
font-family: inherit; font-size: var(--text-sm); font-weight: var(--fw-medium); cursor: pointer;
|
|
1301
|
+
}
|
|
1302
|
+
.tab-btn:hover { color: var(--text); }
|
|
1303
|
+
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
|
|
1304
|
+
.tabpane { display: none; }
|
|
1305
|
+
.tabpane.active { display: block; }
|
|
1306
|
+
|
|
1307
|
+
/* ── Mermaid diagrams ────────────────────────────────────────────────────── */
|
|
1308
|
+
.content figure { margin: var(--sp-4) 0; text-align: center; }
|
|
1309
|
+
.content figure svg, .content > svg, .content .mermaid svg { max-width: 100%; height: auto; }
|
|
1310
|
+
|
|
1311
|
+
/* ── Index page: category card grid ──────────────────────────────────────── */
|
|
1312
|
+
.content .lead { color: var(--text-muted); font-size: var(--text-base); max-width: 60ch; margin-top: var(--sp-2); }
|
|
1313
|
+
.guide-cards {
|
|
1314
|
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(min(var(--card-min), 100%), 1fr));
|
|
1315
|
+
gap: var(--sp-4); margin: var(--sp-4) 0 var(--sp-6);
|
|
1316
|
+
}
|
|
1317
|
+
.guide-card {
|
|
1318
|
+
display: flex; flex-direction: column; gap: var(--sp-2);
|
|
1319
|
+
padding: var(--sp-4); background: var(--bg-card);
|
|
1320
|
+
border: 1px solid var(--border); border-radius: var(--radius);
|
|
1321
|
+
color: inherit; text-decoration: none;
|
|
1322
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
1323
|
+
}
|
|
1324
|
+
.guide-card:hover { border-color: var(--accent); box-shadow: var(--shadow-md); text-decoration: none; }
|
|
1325
|
+
.guide-card-title { font-weight: var(--fw-semi); color: var(--accent); font-size: var(--text-base); }
|
|
1326
|
+
.guide-card-desc { color: var(--text-muted); font-size: var(--text-sm); line-height: var(--lh-normal); }
|
|
1327
|
+
|
|
1328
|
+
/* ── Responsive: TOC becomes an off-canvas drawer (chrome.ts toggles .open) ──*/
|
|
1329
|
+
/* 860px is literal — media queries cannot read custom properties. Revisit it if
|
|
1330
|
+
--topbar-h / --rail-w / --drawer-w change (the drawer sticky offsets key off them). */
|
|
1331
|
+
@media (max-width: 860px) {
|
|
1332
|
+
.nav-toggle { display: inline-flex; align-items: center; }
|
|
1333
|
+
.layout { grid-template-columns: 1fr; gap: 0; }
|
|
1334
|
+
.rail {
|
|
1335
|
+
position: fixed; top: var(--topbar-h); left: 0; bottom: 0; width: var(--drawer-w); z-index: 50;
|
|
1336
|
+
height: auto; background: var(--bg-card); border-right: 1px solid var(--border);
|
|
1337
|
+
padding: var(--sp-4); box-shadow: var(--shadow-lg);
|
|
1338
|
+
transform: translateX(-100%); transition: transform 0.2s ease, visibility 0.2s ease;
|
|
1339
|
+
/* Closed drawer is off-screen AND removed from tab order / pointer events. */
|
|
1340
|
+
visibility: hidden; pointer-events: none;
|
|
1341
|
+
}
|
|
1342
|
+
.rail.open { transform: translateX(0); visibility: visible; pointer-events: auto; }
|
|
1343
|
+
.rail-close {
|
|
1344
|
+
display: block; margin-left: auto; margin-bottom: var(--sp-2);
|
|
1345
|
+
background: var(--bg-card); border: 1px solid var(--border); color: var(--text);
|
|
1346
|
+
border-radius: var(--radius-sm); padding: var(--sp-1) var(--sp-3); font-size: var(--text-base);
|
|
1347
|
+
line-height: 1; cursor: pointer; font-family: inherit;
|
|
1348
|
+
}
|
|
1349
|
+
.rail-close:hover { background: var(--bg-hover); border-color: var(--accent); }
|
|
1350
|
+
.rail-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
1351
|
+
.rail-backdrop {
|
|
1352
|
+
display: block; position: fixed; inset: var(--topbar-h) 0 0 0;
|
|
1353
|
+
background: var(--scrim); z-index: 49;
|
|
1354
|
+
opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
|
|
1355
|
+
}
|
|
1356
|
+
.rail.open ~ .rail-backdrop { opacity: 1; pointer-events: auto; }
|
|
1357
|
+
}
|
|
1081
1358
|
</style>
|
|
1082
1359
|
<script>(function(){try{var t=localStorage.getItem('guide-theme');if(!t&&window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches)t='dark';if(t)document.documentElement.setAttribute('data-theme',t);}catch(e){}})();</script>
|
|
1083
1360
|
</head>
|
|
1084
1361
|
<body>
|
|
1085
1362
|
<header class="topbar">
|
|
1086
|
-
<button data-action="nav" class="nav-toggle" aria-label="Toggle navigation"
|
|
1363
|
+
<button data-action="nav" class="nav-toggle" aria-label="Toggle navigation"
|
|
1364
|
+
aria-expanded="false" aria-controls="guide-toc">☰</button>
|
|
1087
1365
|
<h1>MMR Reference</h1>
|
|
1088
1366
|
<button data-action="theme" class="theme-toggle" aria-label="Toggle theme">◐</button>
|
|
1089
1367
|
</header>
|
|
1090
1368
|
<div class="layout">
|
|
1091
|
-
<aside class="rail"
|
|
1369
|
+
<aside class="rail" id="guide-toc">
|
|
1370
|
+
<button class="rail-close" data-action="nav" aria-label="Close navigation">✕</button>
|
|
1371
|
+
<nav class="toc" aria-label="Table of contents"><ul><li class="toc-2"><a href="#what-mmr-is">What MMR is</a></li><li class="toc-3"><a href="#the-core-idea-in-five-moves">The core idea in five moves</a></li><li class="toc-2"><a href="#end-to-end-flow">End-to-end flow</a></li><li class="toc-2"><a href="#the-mmr-review-command">The mmr review command</a></li><li class="toc-3"><a href="#copy-paste-commands-by-target">Copy-paste commands by target</a></li><li class="toc-2"><a href="#other-subcommands">Other subcommands</a></li><li class="toc-2"><a href="#channel-architecture">Channel architecture</a></li><li class="toc-3"><a href="#the-channel-config-shape">The channel config shape</a></li><li class="toc-3"><a href="#built-in-channels">Built-in channels</a></li><li class="toc-3"><a href="#the-dispatcher">The dispatcher</a></li><li class="toc-2"><a href="#scaffold-wrappers">Scaffold wrappers</a></li><li class="toc-2"><a href="#findings-reconciliation-verdicts">Findings, reconciliation & verdicts</a></li><li class="toc-3"><a href="#the-finding-shape">The Finding shape</a></li><li class="toc-3"><a href="#stable-identity-finding-key">Stable identity (finding_key)</a></li><li class="toc-3"><a href="#agreement-confidence">Agreement & confidence</a></li><li class="toc-3"><a href="#the-gate-the-four-verdicts">The gate & the four verdicts</a></li><li class="toc-2"><a href="#degraded-mode-compensation-auth">Degraded mode, compensation & auth</a></li><li class="toc-2"><a href="#configuration-mmryaml">Configuration (.mmr.yaml)</a></li></ul></nav>
|
|
1372
|
+
</aside>
|
|
1092
1373
|
<main class="content"><h2 id="what-mmr-is">What MMR is</h2>
|
|
1093
1374
|
<p>Multi-Model Review runs your changes past several <strong>independent</strong> AI code
|
|
1094
1375
|
reviewers ("channels"), then <strong>reconciles</strong> their findings into a single
|
|
@@ -1099,12 +1380,12 @@ disagreement is what surfaces ambiguity.</p>
|
|
|
1099
1380
|
<ol>
|
|
1100
1381
|
<li><strong>Resolve a diff</strong> — from a PR, staged changes, a branch range, or a piped diff.</li>
|
|
1101
1382
|
<li><strong>Dispatch channels</strong> — each channel is a separate subprocess given the same
|
|
1102
|
-
prompt, run in parallel and isolated
|
|
1383
|
+
prompt, run in parallel and isolated <span class="fp" data-path="packages/mmr/src/commands/review.ts:636">packages/mmr/src/commands/review.ts:636</span>.</li>
|
|
1103
1384
|
<li><strong>Parse</strong> — each channel's raw output is parsed into a common <code>Finding</code> shape.</li>
|
|
1104
1385
|
<li><strong>Reconcile</strong> — findings are grouped by a stable key, de-duplicated, and
|
|
1105
|
-
scored for agreement and confidence
|
|
1386
|
+
scored for agreement and confidence <span class="fp" data-path="packages/mmr/src/core/reconciler.ts:43">packages/mmr/src/core/reconciler.ts:43</span>.</li>
|
|
1106
1387
|
<li><strong>Verdict</strong> — a severity gate yields <code>pass</code>, <code>degraded-pass</code>, <code>blocked</code>, or
|
|
1107
|
-
<code>needs-user-decision</code>.</li>
|
|
1388
|
+
<code>needs-user-decision</code> <span class="fp" data-path="packages/mmr/src/types.ts:25">packages/mmr/src/types.ts:25</span>.</li>
|
|
1108
1389
|
</ol>
|
|
1109
1390
|
<div class="callout callout-tip"><p><strong>Two layers, one mental model.</strong> The <code>mmr</code> CLI is the engine that dispatches
|
|
1110
1391
|
the built-in channels and computes the verdict. The <code>scaffold run review-pr</code> /
|
|
@@ -1231,7 +1512,7 @@ table.</p>
|
|
|
1231
1512
|
|
|
1232
1513
|
|
|
1233
1514
|
|
|
1234
|
-
<table><thead><tr><th>Flag</th><th>Group</th><th>Description</th></tr></thead><tbody><tr><td><code>--diff <path|-></code></td><td>input</td><td>Read a unified diff from a file, or <code>-</code> for stdin. Highest-priority input mode.</td></tr><tr><td><code>--pr <n></code></td><td>input</td><td>Fetch the PR diff via <code>gh pr diff</code>.</td></tr><tr><td><code>--staged</code></td><td>input</td><td>Review staged changes (<code>git diff --cached</code>).</td></tr><tr><td><code>--base <ref> [--head <ref>]</code></td><td>input</td><td>Review a branch range (<code>git diff base...head</code>, head defaults to HEAD).</td></tr><tr><td><em>(no input flag)</em></td><td>input</td><td>Falls back to unstaged working-tree changes (<code>git diff</code>).</td></tr><tr><td><code>--focus <text></code></td><td>control</td><td>Free-text focus areas appended to every channel prompt.</td></tr><tr><td><code>--fix-threshold <P0|P1|P2|P3></code></td><td>control</td><td>Severity gate. Findings at or above this block. Default P2 (from <code>.mmr.yaml</code>).</td></tr><tr><td><code>--channels <names…></code></td><td>control</td><td>Run only these channels, overriding config defaults. Abstract channels are filtered out.</td></tr><tr><td><code>--timeout <seconds></code></td><td>control</td><td>Per-channel timeout override.</td></tr><tr><td><code>--template <name></code></td><td>control</td><td>Use a named review-criteria template from config.</td></tr><tr><td><code>--format <json|text|markdown></code></td><td>output</td><td>Output format. Default <code>json</code>.</td></tr><tr><td><code>--sync</code></td><td>mode</td><td>Run the full pipeline (dispatch → parse → reconcile → verdict) and return results. Without it, dispatch is fire-and-forget.</td></tr><tr><td><code>--dry-run</code></td><td>mode</td><td>Resolve the diff and assemble the prompt without dispatching any channel.</td></tr><tr><td><code>--session <id></code></td><td>rounds</td><td>Link this run into a multi-round session
|
|
1515
|
+
<table><thead><tr><th>Flag</th><th>Group</th><th>Description</th></tr></thead><tbody><tr><td><code>--diff <path|-></code></td><td>input</td><td>Read a unified diff from a file, or <code>-</code> for stdin. Highest-priority input mode.</td></tr><tr><td><code>--pr <n></code></td><td>input</td><td>Fetch the PR diff via <code>gh pr diff</code>.</td></tr><tr><td><code>--staged</code></td><td>input</td><td>Review staged changes (<code>git diff --cached</code>).</td></tr><tr><td><code>--base <ref> [--head <ref>]</code></td><td>input</td><td>Review a branch range (<code>git diff base...head</code>, head defaults to HEAD).</td></tr><tr><td><em>(no input flag)</em></td><td>input</td><td>Falls back to unstaged working-tree changes (<code>git diff</code>).</td></tr><tr><td><code>--focus <text></code></td><td>control</td><td>Free-text focus areas appended to every channel prompt.</td></tr><tr><td><code>--fix-threshold <P0|P1|P2|P3></code></td><td>control</td><td>Severity gate. Findings at or above this block. Default P2 (from <code>.mmr.yaml</code>).</td></tr><tr><td><code>--channels <names…></code></td><td>control</td><td>Run only these channels, overriding config defaults. Abstract channels are filtered out.</td></tr><tr><td><code>--timeout <seconds></code></td><td>control</td><td>Per-channel timeout override.</td></tr><tr><td><code>--template <name></code></td><td>control</td><td>Use a named review-criteria template from config.</td></tr><tr><td><code>--format <json|text|markdown></code></td><td>output</td><td>Output format. Default <code>json</code>.</td></tr><tr><td><code>--sync</code></td><td>mode</td><td>Run the full pipeline (dispatch → parse → reconcile → verdict) and return results. Without it, dispatch is fire-and-forget.</td></tr><tr><td><code>--dry-run</code></td><td>mode</td><td>Resolve the diff and assemble the prompt without dispatching any channel.</td></tr><tr><td><code>--session <id></code></td><td>rounds</td><td>Link this run into a multi-round session; the id must match <code>^[A-Za-z0-9_-]+$</code> and not be a reserved name <span class="fp" data-path="packages/mmr/src/commands/sessions.ts:15">packages/mmr/src/commands/sessions.ts:15</span>.</td></tr><tr><td><code>--round <n></code></td><td>rounds</td><td>1-based round counter within a session.</td></tr><tr><td><code>--max-rounds <n></code></td><td>rounds</td><td>Hard cap on rounds. Defaults to 5 when <code>--session</code> is set without it.</td></tr><tr><td><code>--accept-new-acks</code></td><td>trust</td><td>Trust acknowledgment files newly introduced by the diff.</td></tr><tr><td><code>--trust-project-acks</code></td><td>trust</td><td>Trust working-tree project acks in non-Git / untrusted modes.</td></tr><tr><td><code>--trust-project-config</code></td><td>trust</td><td>Trust working-tree <code>.mmr.yaml</code> in untrusted modes.</td></tr><tr><td><code>--config-base-ref <ref></code></td><td>trust</td><td>Load <code>.mmr.yaml</code> and acks from a trusted Git ref instead of HEAD.</td></tr></tbody></table></div>
|
|
1235
1516
|
<h3 id="copy-paste-commands-by-target">Copy-paste commands by target</h3>
|
|
1236
1517
|
<pre><code class="language-bash"># PR review (full pipeline, JSON out)
|
|
1237
1518
|
mmr review --pr 123 --sync --format json
|
|
@@ -1289,7 +1570,7 @@ mmr review --pr 123 --channels grok claude --sync --format json
|
|
|
1289
1570
|
|
|
1290
1571
|
|
|
1291
1572
|
|
|
1292
|
-
<table><thead><tr><th>Command</th><th>Purpose</th></tr></thead><tbody><tr><td><code>mmr reconcile <job-id> --channel <name> --input <data></code></td><td>Inject an external channel's findings (e.g. the Superpowers agent) into an existing job and re-run the results pipeline. Input is a file, <code>-</code> for stdin, or inline JSON
|
|
1573
|
+
<table><thead><tr><th>Command</th><th>Purpose</th></tr></thead><tbody><tr><td><code>mmr reconcile <job-id> --channel <name> --input <data></code></td><td>Inject an external channel's findings (e.g. the Superpowers agent) into an existing job and re-run the results pipeline. Input is a file, <code>-</code> for stdin, or inline JSON. <span class="fp" data-path="packages/mmr/src/commands/reconcile.ts:17">packages/mmr/src/commands/reconcile.ts:17</span></td></tr><tr><td><code>mmr status <job-id></code></td><td>Per-channel status and elapsed time. Exit 0 = all complete, 1 = running, 2 = a channel failed, 5 = not found.</td></tr><tr><td><code>mmr results <job-id> [--raw]</code></td><td>Re-run parse → reconcile → format on a completed job. Exit code reflects the verdict.</td></tr><tr><td><code>mmr jobs <list|prune></code></td><td>List jobs, or prune old ones per <code>job_retention_days</code>.</td></tr><tr><td><code>mmr sessions <start|list|show|end> <id></code></td><td>Manage multi-round review sessions (stored under <code>~/.mmr/sessions/</code>).</td></tr><tr><td><code>mmr config <init|show|validate…></code></td><td>Scaffold and inspect <code>.mmr.yaml</code> (including OSS-runtime example blocks).</td></tr><tr><td><code>mmr ack <add|list|rm|prune></code></td><td>Sticky acknowledgments — silence a finding by its stable key so it stops blocking across rounds.</td></tr></tbody></table>
|
|
1293
1574
|
<pre><code class="language-bash"># Capture a job_id from a review, then fold in an agent channel:
|
|
1294
1575
|
mmr reconcile "$JOB_ID" --channel superpowers --input findings.json
|
|
1295
1576
|
</code></pre>
|
|
@@ -1321,7 +1602,7 @@ uses <code>prompt_delivery: prompt-file</code> — the dispatcher writes the pro
|
|
|
1321
1602
|
file and passes its path via the <code>{{prompt_file}}</code> placeholder. Grok wraps its
|
|
1322
1603
|
reply in a JSON <code>.text</code> field, which the parser unwraps before extracting
|
|
1323
1604
|
findings.</p></div>
|
|
1324
|
-
<div class="tabs"><div class="tablist" role="tablist"><button class="tab-btn active" role="tab" data-tab="0">Compare</button><button class="tab-btn" role="tab" data-tab="1">codex</button><button class="tab-btn" role="tab" data-tab="2">gemini</button><button class="tab-btn" role="tab" data-tab="3">claude</button><button class="tab-btn" role="tab" data-tab="4">grok</button><button class="tab-btn" role="tab" data-tab="5">doc-conformance</button></div><div class="tabpane active" data-tab="0">
|
|
1605
|
+
<div class="tabs"><div class="tablist" role="tablist"><button id="tab-0-0" class="tab-btn active" role="tab" data-tab="0" aria-controls="tabpane-0-0" aria-selected="true" tabindex="0">Compare</button><button id="tab-0-1" class="tab-btn" role="tab" data-tab="1" aria-controls="tabpane-0-1" aria-selected="false" tabindex="-1">codex</button><button id="tab-0-2" class="tab-btn" role="tab" data-tab="2" aria-controls="tabpane-0-2" aria-selected="false" tabindex="-1">gemini</button><button id="tab-0-3" class="tab-btn" role="tab" data-tab="3" aria-controls="tabpane-0-3" aria-selected="false" tabindex="-1">claude</button><button id="tab-0-4" class="tab-btn" role="tab" data-tab="4" aria-controls="tabpane-0-4" aria-selected="false" tabindex="-1">grok</button><button id="tab-0-5" class="tab-btn" role="tab" data-tab="5" aria-controls="tabpane-0-5" aria-selected="false" tabindex="-1">doc-conformance</button></div><div id="tabpane-0-0" class="tabpane active" role="tabpanel" data-tab="0" aria-labelledby="tab-0-0" tabindex="0"><p>The defaults, commands, and parsers below are the built-in presets <span class="fp" data-path="packages/mmr/src/config/defaults.ts:32">packages/mmr/src/config/defaults.ts:32</span>.</p>
|
|
1325
1606
|
|
|
1326
1607
|
|
|
1327
1608
|
|
|
@@ -1368,25 +1649,25 @@ findings.</p></div>
|
|
|
1368
1649
|
|
|
1369
1650
|
|
|
1370
1651
|
|
|
1371
|
-
<table><thead><tr><th>Channel</th><th>Default</th><th>Strength</th><th>Prompt delivery</th><th>Parser</th></tr></thead><tbody><tr><td><code>codex</code></td><td>enabled</td><td>Correctness, security, API contracts</td><td>stdin</td><td><code>default</code></td></tr><tr><td><code>gemini</code></td><td>enabled</td><td>Architecture, broad-context reasoning</td><td>stdin</td><td><code>gemini</code></td></tr><tr><td><code>claude</code></td><td>enabled</td><td>Plan alignment, code quality, testing</td><td>stdin</td><td><code>default</code></td></tr><tr><td><code>grok</code></td><td>enabled</td><td>Independent second opinion (xAI; proprietary)</td><td><strong>prompt-file</strong></td><td><code>unwrap $.text → default</code></td></tr><tr><td><code>doc-conformance</code></td><td>opt-in</td><td>PRD/stories/standards conformance (LLM-graded)</td><td>stdin</td><td><code>doc-conformance</code></td></tr></tbody></table></div><div class="tabpane" data-tab="1"><pre><code class="language-yaml">command: codex exec
|
|
1652
|
+
<table><thead><tr><th>Channel</th><th>Default</th><th>Strength</th><th>Prompt delivery</th><th>Parser</th></tr></thead><tbody><tr><td><code>codex</code></td><td>enabled</td><td>Correctness, security, API contracts</td><td>stdin</td><td><code>default</code></td></tr><tr><td><code>gemini</code></td><td>enabled</td><td>Architecture, broad-context reasoning</td><td>stdin</td><td><code>gemini</code></td></tr><tr><td><code>claude</code></td><td>enabled</td><td>Plan alignment, code quality, testing</td><td>stdin</td><td><code>default</code></td></tr><tr><td><code>grok</code></td><td>enabled</td><td>Independent second opinion (xAI; proprietary)</td><td><strong>prompt-file</strong></td><td><code>unwrap $.text → default</code></td></tr><tr><td><code>doc-conformance</code></td><td>opt-in</td><td>PRD/stories/standards conformance (LLM-graded)</td><td>stdin</td><td><code>doc-conformance</code></td></tr></tbody></table></div><div id="tabpane-0-1" class="tabpane" role="tabpanel" data-tab="1" aria-labelledby="tab-0-1" tabindex="0"><pre><code class="language-yaml">command: codex exec
|
|
1372
1653
|
flags: [--skip-git-repo-check, -s, read-only, --ephemeral]
|
|
1373
1654
|
auth.check: codex login status # local file check (fast, 5s)
|
|
1374
1655
|
recovery: codex login
|
|
1375
1656
|
output_parser: default
|
|
1376
1657
|
stderr: suppress
|
|
1377
|
-
</code></pre></div><div class="tabpane" data-tab="2"><pre><code class="language-yaml">command: gemini # NO -p: gemini reads stdin natively
|
|
1658
|
+
</code></pre></div><div id="tabpane-0-2" class="tabpane" role="tabpanel" data-tab="2" aria-labelledby="tab-0-2" tabindex="0"><pre><code class="language-yaml">command: gemini # NO -p: gemini reads stdin natively
|
|
1378
1659
|
flags: [--output-format, json]
|
|
1379
1660
|
env: { NO_BROWSER: "true" }
|
|
1380
1661
|
auth.check: NO_BROWSER=true gemini -p "respond with ok" -o json # LLM round-trip, 20s
|
|
1381
1662
|
recovery: gemini -p "hello"
|
|
1382
1663
|
output_parser: gemini # unwraps { "response": "…" }
|
|
1383
1664
|
timeout: 360
|
|
1384
|
-
</code></pre></div><div class="tabpane" data-tab="3"><pre><code class="language-yaml">command: claude -p
|
|
1665
|
+
</code></pre></div><div id="tabpane-0-3" class="tabpane" role="tabpanel" data-tab="3" aria-labelledby="tab-0-3" tabindex="0"><pre><code class="language-yaml">command: claude -p
|
|
1385
1666
|
flags: [--output-format, json]
|
|
1386
1667
|
auth.check: claude -p "respond with ok" # LLM round-trip, 20s
|
|
1387
1668
|
recovery: claude login
|
|
1388
1669
|
output_parser: default
|
|
1389
|
-
</code></pre></div><div class="tabpane" data-tab="4"><pre><code class="language-yaml">command: grok
|
|
1670
|
+
</code></pre></div><div id="tabpane-0-4" class="tabpane" role="tabpanel" data-tab="4" aria-labelledby="tab-0-4" tabindex="0"><pre><code class="language-yaml">command: grok
|
|
1390
1671
|
prompt_delivery: prompt-file
|
|
1391
1672
|
flags: [--prompt-file, "{{prompt_file}}", --output-format, json]
|
|
1392
1673
|
auth.check: grok models # lists models / login state (no round-trip)
|
|
@@ -1394,7 +1675,7 @@ recovery: grok login
|
|
|
1394
1675
|
output_parser: { kind: unwrap-jsonpath, wrap: "$.text", then: default }
|
|
1395
1676
|
</code></pre><p>Grok is proprietary (xAI), not open-source — it joins the standard set
|
|
1396
1677
|
mechanically as a 4th CLI channel. Disable it with
|
|
1397
|
-
<code>channels_disabled: ["grok"]</code>.</p></div><div class="tabpane" data-tab="5"><pre><code class="language-yaml">enabled: false # opt-in: runs up to 3 LLM calls (~3 min)
|
|
1678
|
+
<code>channels_disabled: ["grok"]</code>.</p></div><div id="tabpane-0-5" class="tabpane" role="tabpanel" data-tab="5" aria-labelledby="tab-0-5" tabindex="0"><pre><code class="language-yaml">enabled: false # opt-in: runs up to 3 LLM calls (~3 min)
|
|
1398
1679
|
command: scaffold observe audit --profile=full --scope=all --output-mode=mmr-findings
|
|
1399
1680
|
output_parser: doc-conformance # expects a JSON array of findings
|
|
1400
1681
|
timeout: 240
|
|
@@ -1405,7 +1686,8 @@ timeout: 240
|
|
|
1405
1686
|
to its own output file; channels run in parallel and never share output.</li>
|
|
1406
1687
|
<li><strong>Prompt delivery.</strong> <code>stdin</code> mode pipes the prompt and closes stdin (avoids
|
|
1407
1688
|
<code>E2BIG</code> on large diffs). <code>prompt-file</code> mode writes the prompt to
|
|
1408
|
-
<code><channel>.prompt.txt</code> and substitutes <code>{{prompt_file}}</code> in the flags
|
|
1689
|
+
<code><channel>.prompt.txt</code> and substitutes <code>{{prompt_file}}</code> in the flags
|
|
1690
|
+
<span class="fp" data-path="packages/mmr/src/core/dispatcher.ts:79">packages/mmr/src/core/dispatcher.ts:79</span>.</li>
|
|
1409
1691
|
<li><strong>Timeout.</strong> A per-channel timer SIGKILLs the whole process group and marks
|
|
1410
1692
|
the channel <code>timeout</code>.</li>
|
|
1411
1693
|
<li><strong>Command parsing.</strong> <code>command</code> is split on whitespace and spawned without a
|
|
@@ -1417,10 +1699,12 @@ only):</em> a new subprocess channel (<code>command</code> + <code>flags</code>
|
|
|
1417
1699
|
<code>output_parser</code>), output reshaping via the <code>unwrap-jsonpath</code> or
|
|
1418
1700
|
<code>regex-findings</code> parser kinds, disabling/timeout overrides, and pointing the
|
|
1419
1701
|
compensator at a different channel — all pure <code>.mmr.yaml</code>. <em>Needs code:</em> a
|
|
1420
|
-
brand-new <em>named</em> parser must be registered in <code>core/parser.ts</code
|
|
1421
|
-
|
|
1702
|
+
brand-new <em>named</em> parser must be registered in <code>core/parser.ts</code>
|
|
1703
|
+
<span class="cite-advisory" data-path="packages/mmr/src/core/parser.ts:257">packages/mmr/src/core/parser.ts:257</span>; and the
|
|
1422
1704
|
<code>COMPENSATING_FOCUS</code> map carries per-channel focus text (falls back gracefully
|
|
1423
|
-
if absent)
|
|
1705
|
+
if absent). HTTP-endpoint channels (<code>kind: http</code>) are already supported via
|
|
1706
|
+
<code>dispatchHttpChannel</code> — pure <code>.mmr.yaml</code>, no extra code
|
|
1707
|
+
<span class="fp" data-path="packages/mmr/src/config/schema.ts:144">packages/mmr/src/config/schema.ts:144</span>.</p>
|
|
1424
1708
|
<h2 id="scaffold-wrappers">Scaffold wrappers</h2>
|
|
1425
1709
|
<p>Direct <code>mmr review</code> runs the built-in CLI channels. The <code>scaffold run</code> wrappers
|
|
1426
1710
|
add orchestration on top.</p>
|
|
@@ -1455,6 +1739,8 @@ and Grok as foreground Bash calls when the <code>mmr</code> CLI isn't available
|
|
|
1455
1739
|
the background. Background execution produces empty output.</p></div>
|
|
1456
1740
|
<h2 id="findings-reconciliation-verdicts">Findings, reconciliation & verdicts</h2>
|
|
1457
1741
|
<h3 id="the-finding-shape">The Finding shape</h3>
|
|
1742
|
+
<p>Every channel's output parses into this common shape
|
|
1743
|
+
<span class="fp" data-path="packages/mmr/src/types.ts:45">packages/mmr/src/types.ts:45</span>.</p>
|
|
1458
1744
|
<pre><code class="language-json">{
|
|
1459
1745
|
"id": "F-001",
|
|
1460
1746
|
"category": "security",
|
|
@@ -1464,16 +1750,23 @@ the background. Background execution produces empty output.</p></div>
|
|
|
1464
1750
|
"suggestion": "…"
|
|
1465
1751
|
}
|
|
1466
1752
|
</code></pre>
|
|
1467
|
-
<p>
|
|
1468
|
-
<code>
|
|
1469
|
-
cross-round matching), and
|
|
1753
|
+
<p>The <code>location</code> above (<code>src/auth.ts:42</code>) is illustrative. After reconciliation,
|
|
1754
|
+
each finding also carries <code>confidence</code>, <code>sources[]</code>, <code>agreement</code>, a stable
|
|
1755
|
+
<code>finding_key</code>, a <code>description_shingle</code> (for fuzzy cross-round matching), and
|
|
1756
|
+
<code>acknowledged</code> <span class="fp" data-path="packages/mmr/src/types.ts:54">packages/mmr/src/types.ts:54</span>.</p>
|
|
1470
1757
|
<h3 id="stable-identity-finding-key">Stable identity (<code>finding_key</code>)</h3>
|
|
1471
1758
|
<pre><code class="language-text">finding_key = sha1( normLocation | category | sha1(normDescription) | sha1(normSuggestion) )
|
|
1472
1759
|
</code></pre>
|
|
1473
1760
|
<p>Line numbers are stripped from the location and severity is <em>excluded</em>, so the
|
|
1474
|
-
same issue at P1 vs P2 collapses to one key
|
|
1475
|
-
|
|
1761
|
+
same issue at P1 vs P2 collapses to one key
|
|
1762
|
+
<span class="fp" data-path="packages/mmr/src/core/stable-id.ts:115">packages/mmr/src/core/stable-id.ts:115</span>. A character-5-gram shingle backs
|
|
1763
|
+
a Jaccard ≥ 0.7 fuzzy match. Intra-run, findings group by fuzzy shingle overlap
|
|
1764
|
+
<span class="fp" data-path="packages/mmr/src/core/reconciler.ts:83">packages/mmr/src/core/reconciler.ts:83</span>; across rounds, the ack store reuses
|
|
1765
|
+
the same threshold so a re-worded finding still matches a prior ack
|
|
1766
|
+
<span class="fp" data-path="packages/mmr/src/core/ack-store.ts:8">packages/mmr/src/core/ack-store.ts:8</span>.</p>
|
|
1476
1767
|
<h3 id="agreement-confidence">Agreement & confidence</h3>
|
|
1768
|
+
<p>Agreement and confidence are derived per group during reconciliation
|
|
1769
|
+
<span class="fp" data-path="packages/mmr/src/core/reconciler.ts:114">packages/mmr/src/core/reconciler.ts:114</span>.</p>
|
|
1477
1770
|
|
|
1478
1771
|
|
|
1479
1772
|
|
|
@@ -1518,9 +1811,15 @@ Jaccard ≥ 0.7 fuzzy match when wording drifts between rounds.</p>
|
|
|
1518
1811
|
<table><thead><tr><th>Sources</th><th>Severity</th><th>Agreement</th><th>Confidence</th></tr></thead><tbody><tr><td>2+</td><td>same</td><td>consensus</td><td>high</td></tr><tr><td>2+</td><td>differ</td><td>majority</td><td>medium</td></tr><tr><td>1</td><td><span class="sev sev-p0">P0</span></td><td>unique</td><td>high</td></tr><tr><td>1</td><td><code>compensating-*</code></td><td>unique</td><td>low</td></tr><tr><td>1</td><td>other</td><td>unique</td><td>medium</td></tr></tbody></table>
|
|
1519
1812
|
<h3 id="the-gate-the-four-verdicts">The gate & the four verdicts</h3>
|
|
1520
1813
|
<p>The gate <strong>passes</strong> when every unacknowledged finding is <em>below</em> the
|
|
1521
|
-
<code>fix_threshold</code>
|
|
1814
|
+
<code>fix_threshold</code> <span class="fp" data-path="packages/mmr/src/core/reconciler.ts:229">packages/mmr/src/core/reconciler.ts:229</span> (default
|
|
1815
|
+
<span class="sev sev-p2">P2</span> <span class="fp" data-path="packages/mmr/src/config/defaults.ts:16">packages/mmr/src/config/defaults.ts:16</span>). Severity tiers run
|
|
1522
1816
|
<span class="sev sev-p0">P0</span> (highest) → <span class="sev sev-p1">P1</span> → <span class="sev sev-p2">P2</span> →
|
|
1523
1817
|
<span class="sev sev-p3">P3</span> (lowest).</p>
|
|
1818
|
+
<p>The verdict is derived from gate result + channel health, in this branch order:
|
|
1819
|
+
<strong>zero channels completed → <code>needs-user-decision</code></strong>; else a failed gate →
|
|
1820
|
+
<code>blocked</code>; else some channels incomplete → <code>degraded-pass</code>; else <code>pass</code>
|
|
1821
|
+
<span class="fp" data-path="packages/mmr/src/core/reconciler.ts:247">packages/mmr/src/core/reconciler.ts:247</span>. (The no-completed-channels case
|
|
1822
|
+
short-circuits first, so it outranks <code>blocked</code>.)</p>
|
|
1524
1823
|
|
|
1525
1824
|
|
|
1526
1825
|
|
|
@@ -1622,7 +1921,7 @@ channels:
|
|
|
1622
1921
|
when you pass an explicit <code>--channels</code> list).</li>
|
|
1623
1922
|
<li><code>enabled: false</code> — per-channel off switch (how <code>doc-conformance</code> ships).</li>
|
|
1624
1923
|
<li><code>extends</code> — inherit from another channel (≤ 4 levels, cycle-checked); child
|
|
1625
|
-
fields override the parent
|
|
1924
|
+
fields override the parent <span class="fp" data-path="packages/mmr/src/config/loader.ts:145">packages/mmr/src/config/loader.ts:145</span>.</li>
|
|
1626
1925
|
<li><code>fix_threshold</code> — project gate; override per-run with <code>--fix-threshold</code>.</li>
|
|
1627
1926
|
</ul>
|
|
1628
1927
|
<div class="callout callout-danger"><p><strong>Trust boundary.</strong> When reviewing a diff, project <code>.mmr.yaml</code> and acks should
|
|
@@ -1630,6 +1929,7 @@ be read from the diff's <em>base ref</em>, not the working tree — otherwise a
|
|
|
1630
1929
|
add a channel that exfiltrates secrets or self-acknowledge its own findings. Use
|
|
1631
1930
|
<code>--config-base-ref</code> / the <code>--trust-project-*</code> flags to control this in untrusted
|
|
1632
1931
|
(e.g. CI) contexts.</p></div></main>
|
|
1932
|
+
<div class="rail-backdrop" data-action="nav" aria-hidden="true"></div>
|
|
1633
1933
|
</div>
|
|
1634
1934
|
<script>(function(){
|
|
1635
1935
|
var LS_KEY = 'guide-theme';
|
|
@@ -1648,12 +1948,69 @@ add a channel that exfiltrates secrets or self-acknowledge its own findings. Use
|
|
|
1648
1948
|
});
|
|
1649
1949
|
});
|
|
1650
1950
|
|
|
1651
|
-
// ─── Mobile nav
|
|
1951
|
+
// ─── Mobile nav (drawer + backdrop; aria-expanded + Escape-to-close) ──────
|
|
1952
|
+
function setNav(open) {
|
|
1953
|
+
var rail = document.querySelector('.rail');
|
|
1954
|
+
if (rail) rail.classList.toggle('open', open);
|
|
1955
|
+
var toggle = document.querySelector('.nav-toggle');
|
|
1956
|
+
if (toggle) toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
1957
|
+
// Modal-drawer focus containment: while open, make the page content inert
|
|
1958
|
+
// (out of tab order + a11y tree) and move focus into the drawer; on close,
|
|
1959
|
+
// restore content and return focus to the toggle.
|
|
1960
|
+
var main = document.querySelector('.content');
|
|
1961
|
+
if (main) main.inert = open;
|
|
1962
|
+
if (open) {
|
|
1963
|
+
var first = rail && rail.querySelector('a, button, [tabindex]:not([tabindex="-1"])');
|
|
1964
|
+
if (first) first.focus();
|
|
1965
|
+
else if (rail) { rail.setAttribute('tabindex', '-1'); rail.focus(); }
|
|
1966
|
+
} else if (toggle) {
|
|
1967
|
+
toggle.focus();
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
// If the viewport grows past the mobile breakpoint while the drawer is open,
|
|
1971
|
+
// the rail becomes the desktop sidebar and the toggle hides — clear the open
|
|
1972
|
+
// state so .content doesn't stay inert with no way to close it.
|
|
1973
|
+
if (window.matchMedia) {
|
|
1974
|
+
var mq = window.matchMedia('(max-width: 860px)');
|
|
1975
|
+
var onMq = function() {
|
|
1976
|
+
if (mq.matches) return;
|
|
1977
|
+
var rail = document.querySelector('.rail');
|
|
1978
|
+
if (rail) rail.classList.remove('open');
|
|
1979
|
+
var toggle = document.querySelector('.nav-toggle');
|
|
1980
|
+
if (toggle) toggle.setAttribute('aria-expanded', 'false');
|
|
1981
|
+
var main = document.querySelector('.content');
|
|
1982
|
+
if (main) main.inert = false;
|
|
1983
|
+
};
|
|
1984
|
+
if (mq.addEventListener) mq.addEventListener('change', onMq);
|
|
1985
|
+
else if (mq.addListener) mq.addListener(onMq);
|
|
1986
|
+
}
|
|
1652
1987
|
document.querySelectorAll('[data-action="nav"]').forEach(function(btn) {
|
|
1653
1988
|
btn.addEventListener('click', function() {
|
|
1654
1989
|
var rail = document.querySelector('.rail');
|
|
1655
|
-
|
|
1990
|
+
setNav(!(rail && rail.classList.contains('open')));
|
|
1991
|
+
});
|
|
1992
|
+
});
|
|
1993
|
+
// Selecting a TOC link closes the drawer (so the now-active content isn't
|
|
1994
|
+
// left inert behind the panel) before the anchor navigation scrolls.
|
|
1995
|
+
var drawerRail = document.querySelector('.rail');
|
|
1996
|
+
if (drawerRail) {
|
|
1997
|
+
drawerRail.querySelectorAll('a').forEach(function(a) {
|
|
1998
|
+
a.addEventListener('click', function() {
|
|
1999
|
+
if (drawerRail.classList.contains('open')) setNav(false);
|
|
2000
|
+
});
|
|
1656
2001
|
});
|
|
2002
|
+
}
|
|
2003
|
+
document.addEventListener('keydown', function(e) {
|
|
2004
|
+
var rail = document.querySelector('.rail');
|
|
2005
|
+
if (!rail || !rail.classList.contains('open')) return;
|
|
2006
|
+
if (e.key === 'Escape') { setNav(false); return; } // setNav restores focus to the toggle
|
|
2007
|
+
// Trap Tab within the open drawer (modal pattern).
|
|
2008
|
+
if (e.key !== 'Tab') return;
|
|
2009
|
+
var f = rail.querySelectorAll('a[href], button, [tabindex]:not([tabindex="-1"])');
|
|
2010
|
+
if (!f.length) return;
|
|
2011
|
+
var first = f[0], last = f[f.length - 1];
|
|
2012
|
+
if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
|
|
2013
|
+
else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
|
|
1657
2014
|
});
|
|
1658
2015
|
|
|
1659
2016
|
// ─── Copy buttons ─────────────────────────────────────────────────────────
|
|
@@ -1680,17 +2037,31 @@ add a channel that exfiltrates secrets or self-acknowledge its own findings. Use
|
|
|
1680
2037
|
wrapper.insertBefore(btn, pre);
|
|
1681
2038
|
});
|
|
1682
2039
|
|
|
1683
|
-
// ─── Tabs
|
|
2040
|
+
// ─── Tabs (ARIA pattern: aria-selected + roving tabindex + arrow keys) ────
|
|
2041
|
+
function activateTab(group, btn, focus) {
|
|
2042
|
+
var idx = btn.getAttribute('data-tab');
|
|
2043
|
+
group.querySelectorAll('.tab-btn').forEach(function(b) {
|
|
2044
|
+
var on = b === btn;
|
|
2045
|
+
b.classList.toggle('active', on);
|
|
2046
|
+
b.setAttribute('aria-selected', on ? 'true' : 'false');
|
|
2047
|
+
b.setAttribute('tabindex', on ? '0' : '-1');
|
|
2048
|
+
});
|
|
2049
|
+
group.querySelectorAll('.tabpane').forEach(function(pane) {
|
|
2050
|
+
pane.classList.toggle('active', pane.getAttribute('data-tab') === idx);
|
|
2051
|
+
});
|
|
2052
|
+
if (focus) btn.focus();
|
|
2053
|
+
}
|
|
1684
2054
|
document.querySelectorAll('.tabs').forEach(function(group) {
|
|
1685
|
-
group.querySelectorAll('.tab-btn')
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
2055
|
+
var btns = [].slice.call(group.querySelectorAll('.tab-btn'));
|
|
2056
|
+
btns.forEach(function(btn, i) {
|
|
2057
|
+
btn.addEventListener('click', function() { activateTab(group, btn, false); });
|
|
2058
|
+
btn.addEventListener('keydown', function(e) {
|
|
2059
|
+
var ni = -1;
|
|
2060
|
+
if (e.key === 'ArrowRight' || e.key === 'ArrowDown') ni = (i + 1) % btns.length;
|
|
2061
|
+
else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') ni = (i - 1 + btns.length) % btns.length;
|
|
2062
|
+
else if (e.key === 'Home') ni = 0;
|
|
2063
|
+
else if (e.key === 'End') ni = btns.length - 1;
|
|
2064
|
+
if (ni >= 0) { e.preventDefault(); activateTab(group, btns[ni], true); }
|
|
1694
2065
|
});
|
|
1695
2066
|
});
|
|
1696
2067
|
});
|