@codyswann/lisa 2.301.7 → 2.302.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.
Files changed (53) hide show
  1. package/dist/core/upstream-evidence-manifest.js +1 -1
  2. package/package.json +1 -1
  3. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  4. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  5. package/plugins/lisa-agy/plugin.json +1 -1
  6. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  9. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  10. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  11. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  15. package/plugins/lisa-expo-agy/plugin.json +1 -1
  16. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  21. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  26. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  31. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  36. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-rails-agy/plugin.json +1 -1
  41. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  46. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  51. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  53. package/ui/index.html +1240 -0
package/ui/index.html CHANGED
@@ -1437,6 +1437,138 @@
1437
1437
  .dot.warn {
1438
1438
  background: var(--warn);
1439
1439
  }
1440
+ /* --- readiness intake: a Vanta-style answer-the-questions checklist --- */
1441
+ .intake-progress {
1442
+ position: sticky;
1443
+ top: 0;
1444
+ z-index: 20;
1445
+ /* .card has no padding of its own — it relies on .card-head/.card-body
1446
+ wrappers this block bypasses, so pad here directly. */
1447
+ padding: 16px 18px;
1448
+ }
1449
+ .intake-progress .ip-head {
1450
+ display: flex;
1451
+ align-items: baseline;
1452
+ justify-content: space-between;
1453
+ gap: 12px;
1454
+ }
1455
+ .intake-progress .ip-pct {
1456
+ font-size: 26px;
1457
+ font-weight: 700;
1458
+ color: var(--accent);
1459
+ font-variant-numeric: tabular-nums;
1460
+ }
1461
+ .intake-progress .ip-count {
1462
+ font-size: 12px;
1463
+ color: var(--muted);
1464
+ }
1465
+ .intake-bar {
1466
+ margin-top: 10px;
1467
+ height: 8px;
1468
+ border-radius: 99px;
1469
+ background: var(--surface2);
1470
+ overflow: hidden;
1471
+ }
1472
+ .intake-bar-fill {
1473
+ height: 100%;
1474
+ border-radius: 99px;
1475
+ background: var(--accent);
1476
+ transition: width 0.25s ease;
1477
+ }
1478
+ .intake-group .card-head {
1479
+ display: flex;
1480
+ align-items: center;
1481
+ gap: 10px;
1482
+ }
1483
+ .intake-gcount {
1484
+ margin-left: auto;
1485
+ font-size: 11px;
1486
+ font-weight: 650;
1487
+ color: var(--muted);
1488
+ font-variant-numeric: tabular-nums;
1489
+ padding: 2px 8px;
1490
+ border-radius: 99px;
1491
+ background: var(--chip-bg);
1492
+ }
1493
+ .intake-group[data-complete="yes"] .intake-gcount {
1494
+ color: var(--good);
1495
+ background: var(--good-soft);
1496
+ }
1497
+ .intake-item {
1498
+ display: grid;
1499
+ grid-template-columns: 22px 1fr minmax(150px, 240px);
1500
+ /* Align to the top so the dot and the control track the question's
1501
+ first line rather than floating to the middle of a two-line row. */
1502
+ align-items: start;
1503
+ gap: 12px;
1504
+ /* Horizontal padding matches .card-head (18px); .card itself has none. */
1505
+ padding: 12px 18px;
1506
+ border-top: 1px solid var(--line);
1507
+ }
1508
+ .intake-item:first-of-type {
1509
+ border-top: 0;
1510
+ }
1511
+ .intake-dot {
1512
+ width: 18px;
1513
+ height: 18px;
1514
+ /* Nudge onto the cap-height of the question's first line. */
1515
+ margin-top: 1px;
1516
+ flex: none;
1517
+ border-radius: 50%;
1518
+ border: 1.5px solid var(--line-strong);
1519
+ display: inline-flex;
1520
+ align-items: center;
1521
+ justify-content: center;
1522
+ font-size: 11px;
1523
+ line-height: 1;
1524
+ color: transparent;
1525
+ }
1526
+ .intake-item[data-answered="yes"] .intake-dot {
1527
+ background: var(--good);
1528
+ border-color: var(--good);
1529
+ color: #fff;
1530
+ }
1531
+ .intake-item[data-answered="yes"] .intake-dot::after {
1532
+ content: "✓";
1533
+ }
1534
+ .intake-q {
1535
+ font-size: 13px;
1536
+ color: var(--ink);
1537
+ }
1538
+ .intake-help {
1539
+ display: block;
1540
+ margin-top: 2px;
1541
+ font-size: 11px;
1542
+ color: var(--muted);
1543
+ max-width: 60ch;
1544
+ }
1545
+ .intake-item .control {
1546
+ justify-content: flex-end;
1547
+ }
1548
+ .intake-ctls {
1549
+ display: flex;
1550
+ flex-direction: column;
1551
+ gap: 6px;
1552
+ align-items: stretch;
1553
+ }
1554
+ .intake-via select {
1555
+ font-size: 11px;
1556
+ color: var(--muted);
1557
+ }
1558
+ .intake-item .control select,
1559
+ .intake-item .control input.num {
1560
+ width: 100%;
1561
+ }
1562
+ @media (max-width: 720px) {
1563
+ .intake-item {
1564
+ grid-template-columns: 22px 1fr;
1565
+ }
1566
+ .intake-item .control {
1567
+ grid-column: 2;
1568
+ justify-content: flex-start;
1569
+ }
1570
+ }
1571
+
1440
1572
  /* --- staff picker: which coding agent a factory page is read for --- */
1441
1573
  .staff-card {
1442
1574
  padding: 12px 16px;
@@ -1723,6 +1855,10 @@
1723
1855
 
1724
1856
  const DATA = {
1725
1857
  groups: [
1858
+ {
1859
+ label: "Get started",
1860
+ sections: ["readiness"],
1861
+ },
1726
1862
  {
1727
1863
  label: "Project",
1728
1864
  sections: [
@@ -4958,6 +5094,978 @@
4958
5094
  ),
4959
5095
  ],
4960
5096
  };
5097
+ /* ---------------------------------- Readiness (intake) ---------------------------------- */
5098
+ DATA.sections.readiness = {
5099
+ title: "Readiness",
5100
+ desc: "The onboarding questionnaire — Vanta, but for agent-run software. Answer what you use for each thing an autonomous factory depends on, and the meter shows how ready you are. Every question here has a detailed home elsewhere in the console; this is the flat version you fill out first. Nothing is required all at once — an unanswered question is a known gap, not a failure.",
5101
+ src: "intake · maps onto .lisa.config.json",
5102
+ blocks: [
5103
+ {
5104
+ type: "intake",
5105
+ groups: [
5106
+ {
5107
+ label: "The bottom line",
5108
+ note: "The one number the rest of this questionnaire exists to predict.",
5109
+ items: [
5110
+ {
5111
+ q: "What fraction of work ships with zero human touches?",
5112
+ help: "Autonomy rate. Not measuring it is the first gap — every control below is in service of moving this number up without lowering the bar, and you cannot improve what nothing counts.",
5113
+ kind: "select",
5114
+ options: [
5115
+ "We measure it — above 75%",
5116
+ "We measure it — 25–75%",
5117
+ "We measure it — below 25%",
5118
+ "We don't measure it",
5119
+ ],
5120
+ },
5121
+ ],
5122
+ },
5123
+ {
5124
+ label: "Credentials & access",
5125
+ items: [
5126
+ {
5127
+ q: "Where do your agents' secrets live?",
5128
+ help: "The vault of record. Everything else should fetch from here rather than a copy pasted per environment.",
5129
+ kind: "select",
5130
+ value: "1Password",
5131
+ mono: true,
5132
+ options: [
5133
+ "1Password",
5134
+ "Bitwarden Secrets Manager",
5135
+ "Doppler",
5136
+ "Infisical",
5137
+ "HashiCorp Vault",
5138
+ "AWS Secrets Manager",
5139
+ "Azure Key Vault",
5140
+ "GCP Secret Manager",
5141
+ "env vars / .env files",
5142
+ ],
5143
+ },
5144
+ {
5145
+ q: "What sits between the agent and the network?",
5146
+ help: "A credential gateway substitutes real values into outbound requests so the agent only ever handles placeholders — nothing to leak from its context.",
5147
+ kind: "select",
5148
+ mono: true,
5149
+ options: [
5150
+ "none — direct injection",
5151
+ "Vault Agent",
5152
+ "1Password Connect",
5153
+ "Infisical Agent",
5154
+ "custom egress proxy",
5155
+ ],
5156
+ },
5157
+ {
5158
+ q: "Do agents authenticate as themselves, or borrow a human login?",
5159
+ help: "An agent with its own identity is attributable, least-privilege, and revocable without touching yours. Borrowing a human session is why a factory works at a desk and fails unattended.",
5160
+ kind: "select",
5161
+ options: [
5162
+ "Agent has its own identity everywhere",
5163
+ "Agent has its own identity in CI only",
5164
+ "Agent borrows a human session",
5165
+ "Mixed across connections",
5166
+ ],
5167
+ },
5168
+ {
5169
+ q: "How often are static credentials rotated?",
5170
+ help: "A short-lived credential is worth more than a well-hidden one: it has a small blast radius whether or not it leaks.",
5171
+ kind: "number",
5172
+ value: 90,
5173
+ unit: "days",
5174
+ },
5175
+ ],
5176
+ },
5177
+ {
5178
+ label: "Where agents run",
5179
+ items: [
5180
+ {
5181
+ q: "What sandbox do unattended agents run in?",
5182
+ help: "Where work executes when no human is present. This decides the credential path and what the agent can reach.",
5183
+ kind: "select",
5184
+ options: [
5185
+ "Local workstation (scheduled)",
5186
+ "CI runner",
5187
+ "Vendor hosted cloud",
5188
+ "Self-hosted container (E2B / Daytona / Modal)",
5189
+ "Bare metal / VM",
5190
+ ],
5191
+ },
5192
+ {
5193
+ q: "Which coding agents operate this project?",
5194
+ help: "The staff that run the factories. More than one is normal — parity is maintained across all of them.",
5195
+ kind: "multiselect",
5196
+ values: ["claude", "codex"],
5197
+ mono: true,
5198
+ options: [
5199
+ "claude",
5200
+ "codex",
5201
+ "cursor",
5202
+ "agy",
5203
+ "copilot",
5204
+ "opencode",
5205
+ ],
5206
+ },
5207
+ {
5208
+ q: "Do all of those agents get the same guardrails?",
5209
+ help: "A fleet where hooks and rules only exist for one agent is uneven enforcement wearing a uniform. Equal governance means every agent's surface is generated from one source, with drift detection when they diverge.",
5210
+ kind: "select",
5211
+ value: "Generated from one source + drift-checked",
5212
+ options: [
5213
+ "Generated from one source + drift-checked",
5214
+ "Manually mirrored",
5215
+ "Uneven — one agent is governed, the rest ride along",
5216
+ ],
5217
+ },
5218
+ {
5219
+ q: "Is agent network egress restricted?",
5220
+ help: "A gateway only protects what passes through it. Without an egress lock, an agent that can run shell reaches the network directly and bypasses every control.",
5221
+ kind: "select",
5222
+ options: [
5223
+ "Locked to an allowlist / gateway",
5224
+ "Open — any host reachable",
5225
+ ],
5226
+ },
5227
+ ],
5228
+ },
5229
+ {
5230
+ label: "The agent's program",
5231
+ note: "Prompts, skills and models are the factory's own source code and runtime. This group asks whether that layer is under change control — or improvised per run.",
5232
+ items: [
5233
+ {
5234
+ q: "Is production work driven by pre-vetted skills, or free-form prompts?",
5235
+ help: "A skill is a reviewed, versioned procedure; a free-form prompt is an improvised one. Free-prompting production work is editing in production.",
5236
+ kind: "select",
5237
+ value: "Version-controlled skills / commands",
5238
+ options: [
5239
+ "Version-controlled skills / commands",
5240
+ "Mostly skills — ad-hoc allowed",
5241
+ "Free-form prompting",
5242
+ ],
5243
+ },
5244
+ {
5245
+ q: "Do prompt and skill changes go through the same gates as code?",
5246
+ help: "If the prompt is the program, a skill edit is a code change — it should ride the same PR review and CI the code does.",
5247
+ kind: "select",
5248
+ value: "Same PR review + gates",
5249
+ options: [
5250
+ "Same PR review + gates",
5251
+ "Version-controlled, unreviewed",
5252
+ "Edited live",
5253
+ ],
5254
+ },
5255
+ {
5256
+ q: "How is a model upgrade qualified before agents use it?",
5257
+ help: "A model swap changes every factory at once. Unless something regression-tests the agent layer itself — golden tasks, canary runs — the upgrade ships unreviewed.",
5258
+ kind: "select",
5259
+ options: [
5260
+ "Eval suite on golden tasks",
5261
+ "Canary on real work items",
5262
+ "Manual spot-check",
5263
+ "We just upgrade",
5264
+ ],
5265
+ via: [
5266
+ "promptfoo",
5267
+ "Braintrust",
5268
+ "Langfuse / LangSmith",
5269
+ "In-house evals",
5270
+ "None",
5271
+ ],
5272
+ },
5273
+ ],
5274
+ },
5275
+ {
5276
+ label: "Work intake",
5277
+ items: [
5278
+ {
5279
+ q: "Where do product requirements (PRDs) live?",
5280
+ help: "The source the Research factory writes to and the Plan factory reads from.",
5281
+ kind: "select",
5282
+ mono: true,
5283
+ value: "Notion",
5284
+ options: [
5285
+ "Notion",
5286
+ "Confluence",
5287
+ "Linear",
5288
+ "GitHub",
5289
+ "Google Docs",
5290
+ ],
5291
+ },
5292
+ {
5293
+ q: "What tracks work items?",
5294
+ help: "Where PRDs decompose into tickets, and where the build loop claims and closes them.",
5295
+ kind: "select",
5296
+ mono: true,
5297
+ value: "JIRA",
5298
+ options: ["JIRA", "GitHub", "Linear"],
5299
+ },
5300
+ {
5301
+ q: "Is there a knowledge source agents read for project context?",
5302
+ help: "Without one, every factory re-derives how the project is built on each run and cannot know what was already decided and why.",
5303
+ kind: "select",
5304
+ options: [
5305
+ "Lisa LLM Wiki",
5306
+ "Other wiki / docs site",
5307
+ "None — code only",
5308
+ ],
5309
+ },
5310
+ {
5311
+ q: "Is that knowledge source maintained, or rotting?",
5312
+ help: "A wiki written once decays into confident misinformation — worse than nothing, because agents believe it. Maintenance means a convergence loop: open questions tracked as gaps, answered by humans, absorbed, repeated until zero remain.",
5313
+ kind: "select",
5314
+ options: [
5315
+ "Convergence loop — gaps tracked to zero",
5316
+ "Updated ad-hoc",
5317
+ "Written once",
5318
+ ],
5319
+ },
5320
+ {
5321
+ q: "Is incoming work validated before agents build it?",
5322
+ help: "The gate itself. An adversarial intake rejects work that is ambiguous or that the factory cannot prove it has the tooling to execute — and raises what it cannot resolve to a human. Building work as-written means every unclear ticket becomes an improvised guess at scale.",
5323
+ kind: "select",
5324
+ value: "Adversarial gate — ambiguous work rejected",
5325
+ options: [
5326
+ "Adversarial gate — ambiguous work rejected",
5327
+ "Light triage",
5328
+ "Built as written",
5329
+ ],
5330
+ },
5331
+ ],
5332
+ },
5333
+ {
5334
+ label: "Correctness gates",
5335
+ items: [
5336
+ {
5337
+ q: "Is unit behaviour proven on every change?",
5338
+ help: "Not that a test runner exists — that tests actually run and must pass before a merge. A runner nobody's failures block is decoration.",
5339
+ kind: "select",
5340
+ value: "Required to pass in CI",
5341
+ options: [
5342
+ "Required to pass in CI",
5343
+ "Runs but does not block a merge",
5344
+ "Local / manual only",
5345
+ "No automated unit tests",
5346
+ ],
5347
+ via: [
5348
+ "vitest",
5349
+ "jest",
5350
+ "bun test",
5351
+ "mocha",
5352
+ "pytest",
5353
+ "RSpec",
5354
+ "go test",
5355
+ "In-house harness",
5356
+ ],
5357
+ viaValue: "vitest",
5358
+ },
5359
+ {
5360
+ q: "Minimum unit-test coverage you enforce?",
5361
+ help: "Below this the build fails. Thresholds should only ratchet up.",
5362
+ kind: "number",
5363
+ value: 74,
5364
+ unit: "%",
5365
+ },
5366
+ {
5367
+ q: "What drives end-to-end journeys?",
5368
+ help: "Proof the product works for a person, not just that units pass.",
5369
+ kind: "select",
5370
+ mono: true,
5371
+ options: [
5372
+ "Playwright",
5373
+ "Cypress",
5374
+ "Selenium",
5375
+ "Maestro",
5376
+ "Detox",
5377
+ "None",
5378
+ ],
5379
+ },
5380
+ {
5381
+ q: "Do you run mutation testing?",
5382
+ help: "Coverage proves a line ran; mutation proves a test would fail if that line were wrong — it keeps coverage honest.",
5383
+ kind: "select",
5384
+ options: ["Yes — gates merges", "Yes — nightly only", "No"],
5385
+ value: "Yes — nightly only",
5386
+ },
5387
+ {
5388
+ q: "Can a quality threshold be quietly lowered?",
5389
+ help: "The ratchet: thresholds may only tighten, and loosening one requires its own reviewed change. A bar anyone can edit downward in passing is a suggestion, not a bar — and an agent under pressure to go green will find it.",
5390
+ kind: "select",
5391
+ value:
5392
+ "No — ratcheted; loosening needs its own reviewed change",
5393
+ options: [
5394
+ "No — ratcheted; loosening needs its own reviewed change",
5395
+ "Reviewable like any other change",
5396
+ "Anyone can edit the config",
5397
+ ],
5398
+ },
5399
+ ],
5400
+ },
5401
+ {
5402
+ label: "Security gates",
5403
+ items: [
5404
+ {
5405
+ q: "What blocks a committed secret?",
5406
+ kind: "select",
5407
+ mono: true,
5408
+ value: "GitGuardian",
5409
+ options: [
5410
+ "GitGuardian",
5411
+ "gitleaks",
5412
+ "trufflehog",
5413
+ "GitHub push protection",
5414
+ "None",
5415
+ ],
5416
+ },
5417
+ {
5418
+ q: "What does static security analysis?",
5419
+ kind: "select",
5420
+ mono: true,
5421
+ value: "SonarCloud",
5422
+ options: [
5423
+ "SonarCloud",
5424
+ "CodeQL",
5425
+ "Semgrep",
5426
+ "Snyk Code",
5427
+ "None",
5428
+ ],
5429
+ },
5430
+ {
5431
+ q: "What audits dependency vulnerabilities?",
5432
+ kind: "select",
5433
+ mono: true,
5434
+ value: "npm audit + Dependabot",
5435
+ options: [
5436
+ "npm audit + Dependabot",
5437
+ "Snyk",
5438
+ "Trivy",
5439
+ "OSV-Scanner",
5440
+ "None",
5441
+ ],
5442
+ },
5443
+ {
5444
+ q: "What scans your infrastructure code?",
5445
+ help: "Application code passes several security gates; infrastructure often passes none.",
5446
+ kind: "select",
5447
+ mono: true,
5448
+ options: [
5449
+ "cdk-nag",
5450
+ "checkov",
5451
+ "tfsec",
5452
+ "Terrascan",
5453
+ "None",
5454
+ ],
5455
+ },
5456
+ {
5457
+ q: "Are dependency licenses checked against an allowlist?",
5458
+ help: "The legal gate: an agent adding a copyleft dependency to a proprietary codebase is a liability no test suite notices. Agents add dependencies faster than humans ever did.",
5459
+ kind: "select",
5460
+ value: "Enforced in CI",
5461
+ options: [
5462
+ "Enforced in CI",
5463
+ "Audited occasionally",
5464
+ "Not checked",
5465
+ ],
5466
+ via: [
5467
+ "license-checker",
5468
+ "FOSSA",
5469
+ "ScanCode",
5470
+ "Black Duck",
5471
+ "None",
5472
+ ],
5473
+ viaValue: "license-checker",
5474
+ },
5475
+ ],
5476
+ },
5477
+ {
5478
+ label: "Agent attack surface",
5479
+ note: "The group above secures the code; this one secures the agent. Inbound, the risk is untrusted content steering it; outbound, exfiltration — which is governed by the egress lock and credential gateway you answered earlier.",
5480
+ items: [
5481
+ {
5482
+ q: "What stops fetched content from becoming instructions?",
5483
+ help: "Prompt injection: a web page, README, or issue comment the agent reads is untrusted input that may try to steer it. Mitigation means labeling it as data and gating risky tools while it is in context.",
5484
+ kind: "select",
5485
+ options: [
5486
+ "Labeled untrusted + risky tools gated",
5487
+ "Partial mitigations",
5488
+ "Nothing specific",
5489
+ ],
5490
+ },
5491
+ {
5492
+ q: "What can agents read on the open internet?",
5493
+ help: "The inbound counterpart of the egress question — reading the web is the injection surface. Scoping per factory is the sophisticated answer: Research needs the web; a build loop mostly does not.",
5494
+ kind: "select",
5495
+ options: [
5496
+ "Nothing — no web access",
5497
+ "Allowlisted domains only",
5498
+ "Scoped per factory",
5499
+ "Anything",
5500
+ ],
5501
+ },
5502
+ {
5503
+ q: "Can an agent add a new dependency unreviewed?",
5504
+ help: "Models hallucinate plausible package names and attackers register them (slopsquatting), alongside classic typosquats. An agent that can install unreviewed is the softest supply-chain target there is.",
5505
+ kind: "select",
5506
+ options: [
5507
+ "No — review gate + lockfile discipline",
5508
+ "Scanned at install",
5509
+ "Yes — unreviewed",
5510
+ ],
5511
+ via: [
5512
+ "Socket.dev",
5513
+ "Registry allowlist",
5514
+ "Lockfile-only installs",
5515
+ "In-house policy",
5516
+ "None",
5517
+ ],
5518
+ },
5519
+ {
5520
+ q: "Are destructive operations blocked at the tool layer?",
5521
+ help: "Guard hooks that refuse rm -rf, force-push, and history rewrites before they execute — the seatbelt for a fast-typing agent, injected or merely wrong.",
5522
+ kind: "select",
5523
+ value: "Guard hooks block them",
5524
+ options: [
5525
+ "Guard hooks block them",
5526
+ "Sandbox contains them",
5527
+ "Neither",
5528
+ ],
5529
+ },
5530
+ {
5531
+ q: "How are MCP servers and connectors vetted?",
5532
+ help: "A connector is an executable dependency with tool access. A malicious or squatted one is supply chain straight into the agent loop.",
5533
+ kind: "select",
5534
+ options: [
5535
+ "Pinned + reviewed like dependencies",
5536
+ "Ad-hoc — team judgment",
5537
+ "Unvetted",
5538
+ ],
5539
+ },
5540
+ {
5541
+ q: "Are instruction files from cloned repos trusted automatically?",
5542
+ help: "CLAUDE.md, AGENTS.md, hooks and skills in a repo the agent clones are executable instructions somebody else wrote. Obeying them on sight is running unreviewed code — hook-trust prompts exist for exactly this.",
5543
+ kind: "select",
5544
+ options: [
5545
+ "Untrusted until reviewed / trust-gated",
5546
+ "Trusted from known repos only",
5547
+ "Trusted automatically",
5548
+ ],
5549
+ },
5550
+ {
5551
+ q: "Can a bad learning persist and steer future runs?",
5552
+ help: "Agents that keep memory or a learnings ledger can be poisoned slowly — one planted learning today steers every run after it. It is the injection question extended over time; the mitigation is a skeptical gate in front of anything that persists.",
5553
+ kind: "select",
5554
+ value: "Judged gate before anything persists",
5555
+ options: [
5556
+ "Judged gate before anything persists",
5557
+ "Persisted, reviewed occasionally",
5558
+ "Persisted unreviewed",
5559
+ "No persistent memory",
5560
+ ],
5561
+ },
5562
+ {
5563
+ q: "If an agent escapes its sandbox, what can it reach?",
5564
+ help: "The joint test of everything above: scoped credentials, the egress lock and the gateway together decide whether a compromised runtime is an incident or a catastrophe.",
5565
+ kind: "select",
5566
+ options: [
5567
+ "Only what its scoped identity allows",
5568
+ "Some shared infrastructure",
5569
+ "Everything — broad credentials in reach",
5570
+ "Unknown",
5571
+ ],
5572
+ },
5573
+ ],
5574
+ },
5575
+ {
5576
+ label: "Code health",
5577
+ note: "The bar for this whole group: authorless code. A reviewer with the author hidden should not be able to tell who — or what — wrote a change. Anything that gives the author away is an uncodified opinion, and each row below is one class of opinion made enforceable.",
5578
+ items: [
5579
+ {
5580
+ q: "Are file and function size limits enforced?",
5581
+ help: "Bounds on how long a file or a function may get, so nothing grows past what a person — or an agent — can hold in their head.",
5582
+ kind: "select",
5583
+ value: "Enforced in CI",
5584
+ options: [
5585
+ "Enforced in CI",
5586
+ "Advisory (local only)",
5587
+ "Not enforced",
5588
+ ],
5589
+ },
5590
+ {
5591
+ q: "What is your per-function complexity ceiling?",
5592
+ help: "The most common reason a build goes red on otherwise-working code — and the cheapest guard against code an agent can't safely change.",
5593
+ kind: "number",
5594
+ value: 10,
5595
+ },
5596
+ {
5597
+ q: "Is project and module structure enforced?",
5598
+ help: "Architectural rules — import boundaries, required file layouts, where things are allowed to live — so the codebase keeps its shape as agents add to it.",
5599
+ kind: "select",
5600
+ options: [
5601
+ "Enforced in CI",
5602
+ "Advisory (local only)",
5603
+ "Not enforced",
5604
+ ],
5605
+ },
5606
+ {
5607
+ q: "Is your house coding style enforced automatically?",
5608
+ help: "Whatever conventions you've chosen — immutability, functional patterns, error handling, naming. This is elective: having no house style is a valid answer. But a style you've decided on and don't enforce is the exposed case, because an agent will drift from what it was never made to follow.",
5609
+ kind: "select",
5610
+ options: [
5611
+ "Codified and enforced",
5612
+ "Documented but not enforced",
5613
+ "No prescribed house style",
5614
+ ],
5615
+ },
5616
+ {
5617
+ q: "Is dead code detected and removed?",
5618
+ help: "Unused exports and files accumulate fastest under automated authorship, because an agent rarely deletes what it didn't write.",
5619
+ kind: "select",
5620
+ value: "Enforced in CI",
5621
+ options: [
5622
+ "Enforced in CI",
5623
+ "Advisory (local only)",
5624
+ "Not enforced",
5625
+ ],
5626
+ },
5627
+ /* PROPOSED: the authorship probe — /lisa:verify:standards
5628
+ ------------------------------------------------------
5629
+ 1. What to build — an automated version of the question
5630
+ below: take the last N merged diffs, strip authorship,
5631
+ and have a model guess which agent (or person) wrote
5632
+ each. Report accuracy vs chance, and — the useful part
5633
+ — the distinguishing features it keyed on. Each feature
5634
+ is an uncodified opinion and a candidate lint rule.
5635
+ 2. Why — "are our standards complete?" is otherwise
5636
+ unanswerable except by taste. Indistinguishability is
5637
+ the completeness criterion: standards are done when the
5638
+ author disappears from the diff. It is also the
5639
+ stopping rule — it says when to STOP adding rules, not
5640
+ just when to add more.
5641
+ 3. Today — nothing measures this. The house-style question
5642
+ above records whether chosen conventions are enforced;
5643
+ this probe finds the conventions nobody chose.
5644
+ 4. Replaces — nothing; feeds the gardener candidate rules
5645
+ with evidence attached. */
5646
+ {
5647
+ q: "With the author hidden, could a reviewer tell who — or what — wrote a change?",
5648
+ help: "The completeness test for everything above. When output is authorless, agents are interchangeable and a model swap is invisible in the diff. Every tell a reviewer can name is the next rule to codify.",
5649
+ kind: "select",
5650
+ options: [
5651
+ "No — output is indistinguishable",
5652
+ "Usually not",
5653
+ "Yes — authors are recognizable",
5654
+ ],
5655
+ },
5656
+ ],
5657
+ },
5658
+ {
5659
+ label: "Design & UI",
5660
+ note: "The determinism story for anything with a screen — a design system is the linter for UI. Without one an agent improvises every screen; with one it assembles from known parts. A backend or CLI answers “no UI” to each, which counts as answered.",
5661
+ items: [
5662
+ {
5663
+ q: "Do agents build UI from a design system, or style ad-hoc?",
5664
+ help: "A component library plus tokens gives an agent known parts to assemble. Without them, every screen it builds is subtly different — the UI equivalent of no lint rules.",
5665
+ kind: "select",
5666
+ value: "Component library + design tokens",
5667
+ options: [
5668
+ "Component library + design tokens",
5669
+ "A loose set of shared components",
5670
+ "Styled ad-hoc, screen by screen",
5671
+ "No UI in this project",
5672
+ ],
5673
+ },
5674
+ {
5675
+ q: "Are design tokens the source of truth for style?",
5676
+ help: "Spacing, color and type defined once as tokens and consumed everywhere, so an agent cannot hardcode an off-brand value.",
5677
+ kind: "select",
5678
+ options: [
5679
+ "Tokens drive the theme",
5680
+ "Partial — some values hardcoded",
5681
+ "Hardcoded throughout",
5682
+ "No UI in this project",
5683
+ ],
5684
+ },
5685
+ {
5686
+ q: "Is your design tool connected for agents to read?",
5687
+ help: "A live connection — Figma via MCP, say — lets an agent read the real design, its tokens and its assets, instead of working from a screenshot or a description.",
5688
+ kind: "select",
5689
+ options: [
5690
+ "Figma — connected via MCP",
5691
+ "Figma — manual handoff",
5692
+ "Penpot / other tool",
5693
+ "No design tool",
5694
+ "No UI in this project",
5695
+ ],
5696
+ },
5697
+ {
5698
+ q: "Are design components mapped to code components?",
5699
+ help: "Figma Code Connect (or an equivalent) tells the agent which code component a design element actually is, so it reaches for your existing Button instead of rebuilding one. This is the piece that makes design-to-code a lookup rather than a fresh guess every time.",
5700
+ kind: "select",
5701
+ options: [
5702
+ "Mapped via Code Connect",
5703
+ "Documented by hand",
5704
+ "No mapping",
5705
+ "No UI in this project",
5706
+ ],
5707
+ },
5708
+ ],
5709
+ },
5710
+ {
5711
+ label: "Review & merge",
5712
+ items: [
5713
+ {
5714
+ q: "What reviews every diff before merge?",
5715
+ help: "An independent adversarial pass over the change. Provider choice is real here — a tool with resolution semantics can block a merge on unresolved findings; one that only prints cannot.",
5716
+ kind: "multiselect",
5717
+ mono: true,
5718
+ values: ["CodeRabbit", "/code-review"],
5719
+ options: [
5720
+ "CodeRabbit",
5721
+ "Greptile",
5722
+ "Qodo",
5723
+ "/code-review",
5724
+ "Human approval",
5725
+ ],
5726
+ },
5727
+ {
5728
+ q: "Must review findings be resolved before merge?",
5729
+ help: "Review that produces findings nobody must address is commentary. Resolution enforcement means every thread answered — fixed, or replied-to and closed — with the merge blocked server-side until then.",
5730
+ kind: "select",
5731
+ value: "Every thread resolved — server-enforced",
5732
+ options: [
5733
+ "Every thread resolved — server-enforced",
5734
+ "Author's discretion",
5735
+ "Findings are advisory",
5736
+ ],
5737
+ },
5738
+ {
5739
+ q: "Are your quality gates unskippable server-side?",
5740
+ help: "A gate a worker can bypass with --no-verify is advisory. Only branch/push protection cannot be skipped — and an autonomous agent is exactly the worker that will route around a skippable one.",
5741
+ kind: "select",
5742
+ options: [
5743
+ "Yes — required via branch protection",
5744
+ "Partial",
5745
+ "No — advisory only",
5746
+ ],
5747
+ },
5748
+ ],
5749
+ },
5750
+ {
5751
+ label: "Ship & rollback",
5752
+ note: "Change management — the human-shaped hole. In a human SDLC a person approved the deploy and reverted the bad one; every row here is that person's named replacement.",
5753
+ items: [
5754
+ {
5755
+ q: "What stands between a merged PR and production?",
5756
+ kind: "select",
5757
+ value: "Auto-deploy on merge",
5758
+ options: [
5759
+ "Auto-deploy on merge",
5760
+ "Promotion chain (dev → staging → prod)",
5761
+ "Human approval required",
5762
+ "Manual deploys",
5763
+ ],
5764
+ },
5765
+ {
5766
+ q: "Is production deploy gated by a protected environment?",
5767
+ help: "A server-side gate the deploying agent cannot skip — the deploy-time analog of branch protection.",
5768
+ kind: "select",
5769
+ options: [
5770
+ "Protected — human approval",
5771
+ "Protected — automated checks",
5772
+ "Not protected",
5773
+ ],
5774
+ },
5775
+ {
5776
+ q: "Can you roll back a bad deploy?",
5777
+ help: "The Type II question: a rollback that has never been exercised is a hypothesis, not a control.",
5778
+ kind: "select",
5779
+ options: [
5780
+ "Yes — proven by exercise",
5781
+ "In theory — never exercised",
5782
+ "No",
5783
+ ],
5784
+ via: [
5785
+ "Argo Rollouts",
5786
+ "Spinnaker",
5787
+ "Platform-native (Vercel / Amplify)",
5788
+ "git revert + redeploy",
5789
+ "In-house",
5790
+ "None",
5791
+ ],
5792
+ },
5793
+ {
5794
+ q: "How do changes reach users?",
5795
+ help: "Progressive delivery bounds the blast radius of an agent's mistake to a fraction of traffic.",
5796
+ kind: "select",
5797
+ options: [
5798
+ "Feature flags + canary",
5799
+ "Feature flags only",
5800
+ "All-at-once",
5801
+ ],
5802
+ via: [
5803
+ "LaunchDarkly",
5804
+ "Unleash",
5805
+ "Flagsmith",
5806
+ "Statsig",
5807
+ "GrowthBook",
5808
+ "In-house flags",
5809
+ "None",
5810
+ ],
5811
+ },
5812
+ ],
5813
+ },
5814
+ {
5815
+ label: "Verify & acceptance",
5816
+ note: "Processing integrity: green CI proves the tests passed, not that the product works. Something has to use it.",
5817
+ items: [
5818
+ {
5819
+ q: "After a change ships, does anything use the product?",
5820
+ help: "Agent-run UAT — driving real journeys against the deployed build the way a person would.",
5821
+ kind: "select",
5822
+ options: [
5823
+ "Agent-run UAT on every ship",
5824
+ "Smoke tests only",
5825
+ "Green CI is the last check",
5826
+ ],
5827
+ },
5828
+ {
5829
+ q: "Is shipped work checked against its original spec?",
5830
+ help: "Section-by-section conformance against the PRD — the check that catches scope creep and quiet omissions the acceptance criteria miss.",
5831
+ kind: "select",
5832
+ options: ["Every PRD, before it closes", "Sometimes", "No"],
5833
+ },
5834
+ {
5835
+ q: "Does each passing verification become a regression test?",
5836
+ help: "Without codifying, UAT proves the present and protects nothing about the future.",
5837
+ kind: "select",
5838
+ options: [
5839
+ "Yes — codified automatically",
5840
+ "Sometimes",
5841
+ "No",
5842
+ ],
5843
+ },
5844
+ ],
5845
+ },
5846
+ {
5847
+ label: "Observe",
5848
+ items: [
5849
+ {
5850
+ q: "What captures production errors?",
5851
+ help: "The sensor that turns a crash into a build-ready ticket.",
5852
+ kind: "select",
5853
+ mono: true,
5854
+ value: "Sentry",
5855
+ options: [
5856
+ "Sentry",
5857
+ "Rollbar",
5858
+ "Bugsnag",
5859
+ "Crashlytics",
5860
+ "None",
5861
+ ],
5862
+ },
5863
+ {
5864
+ q: "What holds your logs and metrics?",
5865
+ kind: "select",
5866
+ mono: true,
5867
+ value: "AWS CloudWatch",
5868
+ options: [
5869
+ "AWS CloudWatch",
5870
+ "Datadog",
5871
+ "New Relic",
5872
+ "Grafana Cloud",
5873
+ "Splunk",
5874
+ "None",
5875
+ ],
5876
+ },
5877
+ {
5878
+ q: "What tells you what users actually do?",
5879
+ help: "Product analytics is the richest Research input there is — it routes usage patterns back to the Research gate as PRD material.",
5880
+ kind: "select",
5881
+ mono: true,
5882
+ options: [
5883
+ "PostHog",
5884
+ "Amplitude",
5885
+ "Mixpanel",
5886
+ "GA4",
5887
+ "None",
5888
+ ],
5889
+ },
5890
+ ],
5891
+ },
5892
+ {
5893
+ label: "Operate & recover",
5894
+ note: "Availability — the who-notices-at-3am group. Factories fail loudly; sensors and schedulers fail silently, and silence is ambiguous.",
5895
+ items: [
5896
+ {
5897
+ q: "How would you learn a scheduled loop died?",
5898
+ help: "A dead sensor produces nothing — which is pixel-identical to a healthy system with nothing to report. Liveness has to be a signal independent of findings.",
5899
+ kind: "select",
5900
+ options: [
5901
+ "Liveness signal independent of findings",
5902
+ "We'd notice eventually",
5903
+ "We wouldn't",
5904
+ ],
5905
+ via: [
5906
+ "Healthchecks.io",
5907
+ "Cronitor",
5908
+ "Dead Man's Snitch",
5909
+ "In-house heartbeat",
5910
+ "None",
5911
+ ],
5912
+ },
5913
+ {
5914
+ q: "An agent breaks production — what happens?",
5915
+ kind: "select",
5916
+ options: [
5917
+ "Auto-rollback + incident filed",
5918
+ "Alert → a human rolls back",
5919
+ "Ad-hoc — whoever notices",
5920
+ ],
5921
+ via: [
5922
+ "PagerDuty",
5923
+ "Opsgenie",
5924
+ "incident.io",
5925
+ "FireHydrant",
5926
+ "Rootly",
5927
+ "In-house",
5928
+ "None",
5929
+ ],
5930
+ },
5931
+ {
5932
+ q: "When the pipeline itself cannot proceed, does it escalate?",
5933
+ help: "The recovery-required outcome: a decision-ready packet naming the broken capability, not a generic failure alert — and never a silent stall.",
5934
+ kind: "select",
5935
+ options: [
5936
+ "Decision-ready escalation packet",
5937
+ "Generic failure alert",
5938
+ "Silent stall",
5939
+ ],
5940
+ via: [
5941
+ "PagerDuty",
5942
+ "Slack / Teams alert",
5943
+ "ntfy / webhook",
5944
+ "Email",
5945
+ "None",
5946
+ ],
5947
+ },
5948
+ {
5949
+ q: "Does every scheduled loop end each run in a named outcome?",
5950
+ help: "A runbook contract: each run terminates in exactly one of a fixed outcome set (nothing needed, work proposed, change proved, approval requested, recovery required, policy obsolete) with an operator-readable summary. A run that just stops is illegible — and illegible runs are how dead loops hide.",
5951
+ kind: "select",
5952
+ value: "Contract — fixed outcome set + checked-in runbook",
5953
+ options: [
5954
+ "Contract — fixed outcome set + checked-in runbook",
5955
+ "Logs only",
5956
+ "No convention",
5957
+ ],
5958
+ },
5959
+ ],
5960
+ },
5961
+ {
5962
+ label: "Governance & accountability",
5963
+ note: "The audit-trail cluster: everything a human used to vouch for by being in the room.",
5964
+ items: [
5965
+ {
5966
+ q: "Is every agent action attributable to the agent?",
5967
+ help: "Its own identity on commits, tickets and deploys — plus a reconstructable chain from ticket to PR to evidence. Actions appearing as a human is how you lose the audit trail.",
5968
+ kind: "select",
5969
+ options: [
5970
+ "Agent identity + full evidence chain",
5971
+ "Partially",
5972
+ "Actions appear as a human",
5973
+ ],
5974
+ },
5975
+ {
5976
+ q: "Can you answer why any given line of code was written?",
5977
+ help: "The regulated-industry question, and attribution's other half — who is above; this is why. It takes an unbroken chain: line → commit → PR → ticket → PRD → the signal that started it. Every hop that is a convention rather than an enforced link is where the trail goes cold.",
5978
+ kind: "select",
5979
+ value: "Full chain, enforced at commit time",
5980
+ options: [
5981
+ "Full chain, enforced at commit time",
5982
+ "Partial — commits reference tickets",
5983
+ "git blame is the end of the trail",
5984
+ ],
5985
+ },
5986
+ {
5987
+ q: "Does the agent that wrote a change approve its own work?",
5988
+ help: "Segregation of duties, agent edition: review, verification and deploy approval should come from something the author cannot influence.",
5989
+ kind: "select",
5990
+ value: "Never — independent review and verify",
5991
+ options: [
5992
+ "Never — independent review and verify",
5993
+ "Sometimes",
5994
+ "Same agent end to end",
5995
+ ],
5996
+ },
5997
+ {
5998
+ q: "Are agent permissions periodically reviewed?",
5999
+ help: "Access accumulates. In a human org this is the quarterly access review; nothing about removing the human removed the need.",
6000
+ kind: "select",
6001
+ options: [
6002
+ "Scheduled access review",
6003
+ "Ad-hoc",
6004
+ "Never — they accumulate",
6005
+ ],
6006
+ },
6007
+ {
6008
+ q: "Are models pinned, with a plan for vendor outage?",
6009
+ help: "The AI vendor is an availability dependency no human SDLC ever had. An unpinned model is an unreviewed change to every factory at once.",
6010
+ kind: "select",
6011
+ options: [
6012
+ "Pinned + fallback plan",
6013
+ "Pinned only",
6014
+ "Neither",
6015
+ ],
6016
+ },
6017
+ {
6018
+ q: "Are there spend caps and runaway-loop protection?",
6019
+ help: "Agents spend money autonomously. A loop with no budget ceiling is an unbounded liability with a scheduler.",
6020
+ kind: "select",
6021
+ options: [
6022
+ "Per-run and per-loop caps",
6023
+ "Account-level cap only",
6024
+ "None",
6025
+ ],
6026
+ via: [
6027
+ "Vendor usage caps",
6028
+ "LLM gateway metering (LiteLLM / Helicone)",
6029
+ "In-house metering",
6030
+ "None",
6031
+ ],
6032
+ },
6033
+ {
6034
+ q: "Can production data or PII enter an agent's context?",
6035
+ help: "Whatever enters context reaches the model vendor and may persist in transcripts. Confidentiality needs a policy boundary, not good intentions.",
6036
+ kind: "select",
6037
+ options: [
6038
+ "Blocked — policy + redaction",
6039
+ "Limited / case-by-case",
6040
+ "Yes, or unknown",
6041
+ ],
6042
+ via: [
6043
+ "Presidio / redaction pipeline",
6044
+ "Nightfall",
6045
+ "Skyflow",
6046
+ "In-house filters",
6047
+ "None",
6048
+ ],
6049
+ },
6050
+ {
6051
+ q: "Are agent transcripts retained, with an access boundary?",
6052
+ help: "The transcript is the working record of every decision the agent made — audit evidence for the traceability chain, but also a copy of everything it saw. It needs a retention policy and an access boundary like any other sensitive record.",
6053
+ kind: "select",
6054
+ options: [
6055
+ "Retained — policy + access control",
6056
+ "Retained ad-hoc",
6057
+ "Not retained",
6058
+ ],
6059
+ },
6060
+ ],
6061
+ },
6062
+ ],
6063
+ },
6064
+ fLegend(
6065
+ "<b>This is a readiness assessment, not a grade.</b> Like Vanta, it earns its value by measuring something you actually feel — the autonomy rate the first question asks for. An unanswered row is a named gap you can go fix on its detail page; a red one somewhere downstream is a control that is not yet real. And note the shape of the back half: ship, verify, operate, govern are exactly the places a person used to be the control. A no-human SDLC is not one with the humans deleted — it is one where every human-shaped hole has a named, exercised replacement."
6066
+ ),
6067
+ ],
6068
+ };
4961
6069
 
4962
6070
  /* ---------------------------------- General ---------------------------------- */
4963
6071
  /* ---------------------------------- Health ---------------------------------- */
@@ -9547,9 +10655,141 @@
9547
10655
  if (block.type === "plugins-live") return buildPluginsLive(block);
9548
10656
  if (block.type === "staff") return buildStaff(block);
9549
10657
  if (block.type === "environments") return buildEnvironments();
10658
+ if (block.type === "intake") return buildIntake(block);
9550
10659
  return buildCard(block);
9551
10660
  }
9552
10661
 
10662
+ /* The readiness intake: one flat, answerable question per row, grouped by
10663
+ area, with a live completion meter. It is the same ground the factory
10664
+ pages cover, asked as a Vanta-style questionnaire for someone
10665
+ onboarding — answer the questions, watch the bar fill.
10666
+ PROPOSED: each answer should populate the matching config key, so
10667
+ completing the intake configures the detail pages (and vice versa).
10668
+ Not wired here to avoid the option-list mismatch between an intake
10669
+ select — which carries an explicit "not answered" sentinel — and the
10670
+ detail-page selects, which do not. */
10671
+ const INTAKE_UNSET = "— not answered —";
10672
+ function buildIntake(block) {
10673
+ const wrap = el("div", "intake-wrap");
10674
+ const prog = el("div", "card intake-progress");
10675
+ prog.innerHTML =
10676
+ '<div class="ip-head"><span class="ip-pct intake-pct">0%</span>' +
10677
+ '<span class="ip-count intake-count">0 of 0 answered</span></div>' +
10678
+ '<div class="intake-bar"><div class="intake-bar-fill"></div></div>';
10679
+ wrap.appendChild(prog);
10680
+
10681
+ (block.groups || []).forEach(group => {
10682
+ const card = el("div", "card intake-group");
10683
+ const head = el("div", "card-head");
10684
+ head.appendChild(el("h2", "", esc(group.label)));
10685
+ if (group.note) head.appendChild(el("span", "note", esc(group.note)));
10686
+ head.appendChild(el("span", "intake-gcount", "0/0"));
10687
+ card.appendChild(head);
10688
+ (group.items || []).forEach(item => {
10689
+ const row = el("div", "intake-item");
10690
+ row.dataset.kind = item.kind;
10691
+ row.appendChild(el("span", "intake-dot"));
10692
+ const text = el("div");
10693
+ text.appendChild(el("div", "intake-q", esc(item.q)));
10694
+ if (item.help)
10695
+ text.appendChild(el("span", "intake-help", esc(item.help)));
10696
+ row.appendChild(text);
10697
+ // Reuse buildControl so the answer widgets match the rest of the
10698
+ // console and pick up dirty-state tracking.
10699
+ let control;
10700
+ if (item.kind === "multiselect") {
10701
+ control = {
10702
+ type: "multiselect",
10703
+ values: item.values || [],
10704
+ options: item.options,
10705
+ mono: item.mono,
10706
+ };
10707
+ } else if (item.kind === "number") {
10708
+ control = { type: "number", value: item.value, unit: item.unit };
10709
+ } else {
10710
+ control = {
10711
+ type: "select",
10712
+ value: item.value || INTAKE_UNSET,
10713
+ options: [INTAKE_UNSET, ...item.options],
10714
+ mono: item.mono,
10715
+ };
10716
+ }
10717
+ const ctls = el("div", "intake-ctls");
10718
+ ctls.appendChild(buildControl(control, null));
10719
+ // Outcome + mechanism pairing. The outcome select is the claim;
10720
+ // the via select names what implements it — the evidence source a
10721
+ // probe would point at. A claim with no named mechanism is
10722
+ // unverifiable, so a row with a via only counts as answered when
10723
+ // both halves are.
10724
+ if (item.via) {
10725
+ const via = buildControl(
10726
+ {
10727
+ type: "select",
10728
+ value: item.viaValue || INTAKE_UNSET,
10729
+ options: [INTAKE_UNSET, ...item.via],
10730
+ mono: true,
10731
+ },
10732
+ null
10733
+ );
10734
+ via.classList.add("intake-via");
10735
+ ctls.appendChild(via);
10736
+ }
10737
+ row.appendChild(ctls);
10738
+ card.appendChild(row);
10739
+ });
10740
+ wrap.appendChild(card);
10741
+ });
10742
+
10743
+ // Recompute on any answer change, and once now for the initial state.
10744
+ wrap.addEventListener("change", () => refreshIntake(wrap));
10745
+ refreshIntake(wrap);
10746
+ return wrap;
10747
+ }
10748
+
10749
+ function refreshIntake(wrap) {
10750
+ let total = 0;
10751
+ let answered = 0;
10752
+ wrap.querySelectorAll(".intake-group").forEach(group => {
10753
+ let gt = 0;
10754
+ let ga = 0;
10755
+ group.querySelectorAll(".intake-item").forEach(item => {
10756
+ total += 1;
10757
+ gt += 1;
10758
+ const kind = item.dataset.kind;
10759
+ let ok = false;
10760
+ if (kind === "multiselect") {
10761
+ const s = item.querySelector(".ms-summary");
10762
+ ok = !!s && s.textContent.trim() !== "none";
10763
+ } else if (kind === "number") {
10764
+ const i = item.querySelector("input");
10765
+ ok = !!i && i.value.trim() !== "";
10766
+ } else {
10767
+ ok = !!item.querySelector("select");
10768
+ }
10769
+ // Every select in the row — the outcome and any via mechanism —
10770
+ // must be answered. A claim without its named mechanism is not an
10771
+ // answer.
10772
+ const selects = [...item.querySelectorAll("select")];
10773
+ if (selects.some(s => s.value === INTAKE_UNSET)) ok = false;
10774
+ item.dataset.answered = ok ? "yes" : "no";
10775
+ if (ok) {
10776
+ answered += 1;
10777
+ ga += 1;
10778
+ }
10779
+ });
10780
+ const gc = group.querySelector(".intake-gcount");
10781
+ if (gc) gc.textContent = ga + "/" + gt;
10782
+ group.dataset.complete = ga === gt ? "yes" : "no";
10783
+ });
10784
+ const pctVal = total ? Math.round((answered / total) * 100) : 0;
10785
+ const fill = wrap.querySelector(".intake-bar-fill");
10786
+ if (fill) fill.style.width = pctVal + "%";
10787
+ const count = wrap.querySelector(".intake-count");
10788
+ if (count) count.textContent = answered + " of " + total + " answered";
10789
+ const pct = wrap.querySelector(".intake-pct");
10790
+ if (pct) pct.textContent = pctVal + "%";
10791
+ }
10792
+
9553
10793
  /* The staff picker: which coding agent this factory page is read for.
9554
10794
  Single-select — a factory is run by one agent at a time, and its
9555
10795
  readiness is only meaningful with that agent named. */