Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
381 commits
Select commit Hold shift + click to select a range
e0158ef
chore: release v0.88.0 (#1737)
mabry1985 Jul 3, 2026
e07dff4
test(plugins): FakeRegistry.register_surface captures start/stop/relo…
mabry1985 Jul 3, 2026
000fd33
fix(plugins): accept /api/plugins/<id> as a conforming router prefix …
mabry1985 Jul 3, 2026
489fee5
fix(plugins): built-in detection requires a manifest, not just a dir …
mabry1985 Jul 3, 2026
596bbc5
fix(chat): reconnect a dropped provider stream instead of killing the…
mabry1985 Jul 3, 2026
28b73fc
fix(checkpointer): retry a locked checkpoint write instead of dying (…
mabry1985 Jul 3, 2026
1ccff39
feat(plugins): opt-in background auto-update policy (#1720) (#1747)
mabry1985 Jul 3, 2026
d98dab5
feat(coder): solve() can run one forced rung, for testing (v0.2.0) (#…
mabry1985 Jul 3, 2026
abce8d9
feat(plugins): plugin-driven composer forms — #1701 Slice 2 (#1746)
mabry1985 Jul 3, 2026
1e44c37
feat(web): composer-form seam — /effort opens a picker in the compose…
mabry1985 Jul 3, 2026
beb5617
feat(plugins): required-config gate — plugins degrade gracefully when…
mabry1985 Jul 3, 2026
ee8990d
feat(web): plugin setup wizard — ⚠️ needs-setup badge + guided config…
mabry1985 Jul 3, 2026
583b339
fix(watches): honor per-watch interval_s + refresh verifier registry …
mabry1985 Jul 4, 2026
b6841f4
feat(config): SOUL.md version history — auto-snapshot on save + resto…
mabry1985 Jul 4, 2026
973f9e9
feat(observability): tag per-turn telemetry with the active persona r…
mabry1985 Jul 4, 2026
3191b2e
feat(web): SOUL.md version history panel — view + one-click restore (…
mabry1985 Jul 4, 2026
a298cce
fix(web): dedupe rail surfaces by id so a fork surface can't render t…
mabry1985 Jul 4, 2026
21237f1
docs(adr): ADR 0071 — plugin permissions: trust-and-consent, not a sa…
mabry1985 Jul 4, 2026
d77745f
feat(web): quick-chat with any fleet agent from the command palette (…
mabry1985 Jul 4, 2026
8bbb8f2
feat(web): browser-style UI zoom — ⌘/Ctrl +/-/0 (#1711) (#1756)
mabry1985 Jul 4, 2026
190878d
chore: release v0.89.0 (#1763)
mabry1985 Jul 4, 2026
03339fe
feat(plugins): list Career Coach in the plugin catalog (#1765)
mabry1985 Jul 4, 2026
7237513
docs(plugins): point Career Coach marketing docs link at its Quick st…
mabry1985 Jul 4, 2026
c5cf8fa
fix(devkit): testkit host-stubs cover SubagentConfig + Knobs so regis…
mabry1985 Jul 4, 2026
2937abb
fix(console): theme overrides win over defaults + persist across relo…
mabry1985 Jul 4, 2026
e1d16ff
feat(console): surface server-initiated turns with a typing indicator…
mabry1985 Jul 4, 2026
e19c105
feat(chat): swap the add-chat "+" to an incognito eye while Shift is …
mabry1985 Jul 4, 2026
e302c94
chore(web): disable browser-style UI zoom (⌘/Ctrl +/-/0) — revert #17…
mabry1985 Jul 4, 2026
fb3c239
feat(background): batch-join fan-out completions into one push-resume…
mabry1985 Jul 4, 2026
599b3d4
fix(console): chat/composer errors surface as a toast, not an inline …
mabry1985 Jul 4, 2026
14e4fdd
chore: pre-release housekeeping for v0.90.0 — .gitignore + changelog …
mabry1985 Jul 4, 2026
f38ee9d
docs(adr): 0072 — fleet seed, declare a team in config (team-via-conf…
mabry1985 Jul 4, 2026
3f1120e
chore: release v0.90.0 (#1784)
mabry1985 Jul 4, 2026
518fc22
feat(goals): completion contracts — outcome/constraints/boundaries/st…
mabry1985 Jul 4, 2026
0d64164
fix(soul): seed + heal live SOUL.md so a baked persona isn't shadowed…
mabry1985 Jul 4, 2026
30f2afc
feat(fleet): autostart declared members on hub boot (ADR 0072 slice) …
mabry1985 Jul 4, 2026
ca88969
feat(goals): goal-creation form — verifier + completion contract via …
mabry1985 Jul 4, 2026
d35df90
fix(delegates): a2a SendMessage read budget tracks poll_timeout, not …
mabry1985 Jul 4, 2026
71d4810
docs(fleet): add "Deploying a team — config-as-code" section (#1789)
mabry1985 Jul 4, 2026
9934222
feat(memory): make the Injections view legible — plain-language count…
mabry1985 Jul 4, 2026
35cc26d
chore: release v0.91.0 (#1791)
mabry1985 Jul 4, 2026
9f63fd2
fix(marketing): fold scraped plugin cards onto their overrides (#1772…
mabry1985 Jul 5, 2026
0806f86
feat(knowledge): bulk delete-by-source in the Knowledge view (reversi…
mabry1985 Jul 5, 2026
75455e6
feat(palette): toggle fleet agents on/off from ⌘K (#1769) (#1794)
mabry1985 Jul 5, 2026
ac5bc25
feat(lifecycle): system lifecycle events on the bus + hook/config rea…
mabry1985 Jul 5, 2026
8b6c78c
fix(lifecycle): run webhook reactions through the egress guard (#1796)
mabry1985 Jul 5, 2026
9310b40
fix(desktop): wire the bundled CA bundle so web_search works when fro…
mabry1985 Jul 5, 2026
9ca4e2e
chore: release v0.92.0 (#1799)
mabry1985 Jul 5, 2026
7938455
fix(installer): authenticate private GitHub clones on the default git…
mabry1985 Jul 5, 2026
76608f3
feat(fleet): mDNS discovery off by default — agents stay quiet on the…
mabry1985 Jul 5, 2026
af12ad9
fix(plugins): complete the plugin load summary (chat commands + middl…
mabry1985 Jul 5, 2026
f55c749
docs: close the fleet-autostart + surface-resume gap (#1810)
mabry1985 Jul 5, 2026
62ff9a7
feat(artifact): expand @pl/ui to a full design-system component set (…
mabry1985 Jul 5, 2026
0d93e5b
feat(plugins): friction log — the agent records its own rough edges (…
mabry1985 Jul 5, 2026
042a9e3
feat(api): expose incognito on /v1/chat/completions (#1815)
mabry1985 Jul 5, 2026
490426c
feat(mcp): operator-MCP profiles + trust override + HITL-hang fix (AD…
mabry1985 Jul 5, 2026
75231ec
fix(background): dismiss retains the report so Open-report survives (…
mabry1985 Jul 5, 2026
903b907
fix(installer): authenticate the update-check ls-remote for private r…
mabry1985 Jul 5, 2026
7611da8
feat(cli): a first-class `protoagent` command — the terminal control …
mabry1985 Jul 5, 2026
64d52d3
fix(chat): incognito + cue is hover-only, matching quick-delete (#180…
mabry1985 Jul 5, 2026
550de83
feat(tools): quick-disable deep-link from a chat tool card (#1803) (#…
mabry1985 Jul 5, 2026
014f11f
feat(cli): `protoagent model` — point at a local / OpenAI-compatible …
mabry1985 Jul 5, 2026
1887231
feat(knowledge): preview dialog before ingest — confirm what's about …
mabry1985 Jul 5, 2026
b4b9806
feat(api): dedupe the goal API to the canonical plural /api/goals* (A…
mabry1985 Jul 5, 2026
aca24fb
docs(adr): ADR 0075 — external interfaces (protoagent CLI + tighter M…
mabry1985 Jul 5, 2026
35d3ac8
feat(api): GET /api/mcp/exposed — introspect the operator-MCP tool su…
mabry1985 Jul 5, 2026
dd88dc3
feat(v1): real token usage in the OpenAI-compat response (ADR 0075 D4…
mabry1985 Jul 5, 2026
b9eea41
feat(ops): the shared ops layer, starting with knowledge.ingest (ADR …
mabry1985 Jul 5, 2026
c9bdf5d
feat(ops): plugins.install_and_activate — install over the ops layer …
mabry1985 Jul 5, 2026
ae08d83
feat(ops): config + fleet ops — the remaining admin operations (ADR 0…
mabry1985 Jul 5, 2026
1f21bc0
fix(fleet): don't run autostart inside a member (spurious "no workspa…
mabry1985 Jul 5, 2026
3425b4b
feat(ops+cli): operations catalog + protoagent CLI verbs over the ops…
mabry1985 Jul 5, 2026
7b95dbf
feat(cli): `protoagent knowledge ingest` — the third projection of kn…
mabry1985 Jul 5, 2026
667929a
docs: changelog + ADR status + CLI guide for the ops layer (ADR 0075 …
mabry1985 Jul 5, 2026
ce9d738
chore: release v0.93.0 (#1835)
mabry1985 Jul 5, 2026
bbdead1
fix(cli): `protoagent config get` crashed on the default YAML output …
mabry1985 Jul 5, 2026
1af5568
feat(delegates): delegate_to(background=True) — detached delegations …
mabry1985 Jul 5, 2026
d7b55b3
docs(changelog): note delegate_to(background=True) under [0.93.0] (#1…
mabry1985 Jul 5, 2026
386afdc
docs(coding-agents): running an ACP coder in a container, wired to a …
mabry1985 Jul 5, 2026
a0681fb
feat(supervisor): on_crash bounded retry — RetryAfter + on_crash_max_…
mabry1985 Jul 5, 2026
f20d89b
chore(license): MIT license + third-party attribution (#1842)
mabry1985 Jul 5, 2026
0f58229
chore: release v0.93.1 (#1843)
mabry1985 Jul 5, 2026
ed9f52d
docs(coding-agents): parallel builds via a worktree-backed coder pool…
mabry1985 Jul 6, 2026
7f36345
feat(coding-agent): managed git for acp delegates — framework-owned b…
mabry1985 Jul 6, 2026
409fb54
docs(adr): 0076 — managed git for acp delegates (#1845)
mabry1985 Jul 6, 2026
ad4738e
fix(coding-agent): harden the managed-git harness — review findings f…
mabry1985 Jul 6, 2026
e5fdae1
fix(fleet): spawned members run open on loopback — don't inherit the …
mabry1985 Jul 6, 2026
1359dd0
docs(plans): codified delivery loop — two-sprint program breakdown (#…
mabry1985 Jul 6, 2026
22f8df9
feat(review): adversarial code-review workflow + findings convention …
mabry1985 Jul 6, 2026
92ae0ad
feat(craft): adr-authoring skill — the house ADR/plan-doc conventions…
mabry1985 Jul 6, 2026
f9b0875
feat(workflows): due-diligence workflow — codebase-grounded adopt/bui…
mabry1985 Jul 6, 2026
9283870
chore: release v0.94.0 (#1856)
mabry1985 Jul 6, 2026
916eef5
fix(managed-git): infer the branch/PR name from the task, dedup on th…
mabry1985 Jul 6, 2026
92838b1
fix(sdk): run_subagent defaults extra_tools to the host's plugin + MC…
mabry1985 Jul 6, 2026
e6c3f25
fix(review): review-finder turn budget 15 → 25 (#1858)
mabry1985 Jul 6, 2026
5aa675f
feat(review): the noise ledger + Gap semantics + delta re-reviews (AD…
mabry1985 Jul 6, 2026
9e3c477
docs: QA review takeover — plan + ADR 0078 (harvest Quinn, keep the p…
mabry1985 Jul 6, 2026
0e69781
fix(review): keep verify-pass verdicts through the report re-parse (#…
mabry1985 Jul 6, 2026
a999bd5
docs(plan): qa takeover — dual-layer posture, CodeRabbit shadow, prot…
mabry1985 Jul 6, 2026
c264963
feat(review): source attribution on findings — non-LLM panel members …
mabry1985 Jul 6, 2026
e6c6be8
docs(changelog): findings source attribution entry for #1864 (#1865)
mabry1985 Jul 6, 2026
1ddd9ca
chore: release v0.95.0 (#1866)
mabry1985 Jul 6, 2026
95a3ea0
fix(workflows): installed-plugin recipe dirs resolve lazily (ADR 0027…
mabry1985 Jul 6, 2026
eb1de30
fix(review): the report pass carries verify verdicts into the final f…
mabry1985 Jul 6, 2026
f4620b5
docs(plan): B2 shipped — protoPatch seated on the panel, acceptance p…
mabry1985 Jul 6, 2026
9232d82
docs: ADR 0078 Accepted — the QA review takeover program executed (#1…
mabry1985 Jul 6, 2026
c0763a6
chore(beads): bd-13o — QA tier stage-1 shadow rollout tracker (#1871)
mabry1985 Jul 6, 2026
b532c48
docs: add Proxmox reusable LXC template deploy guide (#1873)
mabry1985 Jul 6, 2026
fe89134
fix(a2a): serve the classic v0.3 vocabulary — enable_v0_3_compat on /…
mabry1985 Jul 7, 2026
711c97a
fix(artifact): re-theme the sandboxed frame live on app-theme switch …
mabry1985 Jul 7, 2026
4c663b5
chore: release v0.95.1 (#1877)
mabry1985 Jul 7, 2026
5d39069
fix(subagents): max_turns is a budget, not a bomb — salvage partial o…
mabry1985 Jul 7, 2026
2b9f186
feat(tracing): fleet-wide distributed Langfuse tracing (#1880)
mabry1985 Jul 7, 2026
b4990c7
fleet: members self-advertise their A2A tenant URL (multi-tenancy) (#…
mabry1985 Jul 7, 2026
0f19f20
fleet: agents load their OWN SOUL/persona, not the hub placeholder (#…
mabry1985 Jul 7, 2026
6c70509
fix(plugins): silence routine hot-reload router re-mount, keep real d…
mabry1985 Jul 7, 2026
4f9367e
fix(fleet): set spawned member's own AGENT_NAME instead of inheriting…
mabry1985 Jul 7, 2026
926f1d4
fix(knowledge): retrigger the agent when a background ingest job comp…
mabry1985 Jul 7, 2026
0b70b3d
fix(config): default filesystem.allow_run OFF on the headless UI tier…
mabry1985 Jul 7, 2026
844c7e5
feat(cli): protoagent hermes — one-command Hermes preset + runtime us…
mabry1985 Jul 7, 2026
cdb3e37
fix(runtime): harden the ACP/Hermes runtime path — /v1 switch, instan…
mabry1985 Jul 7, 2026
8960b9f
fix(tracing): emit a fleet-project generation node so an agent's trac…
mabry1985 Jul 7, 2026
0fc9111
fix(fleet): defer plugin-view auth to the member so proxied views sto…
mabry1985 Jul 7, 2026
05486b6
docs(changelog): backfill 9 merged PRs into [Unreleased] before the 0…
mabry1985 Jul 7, 2026
ce76339
chore: release v0.96.0 (#1896)
mabry1985 Jul 7, 2026
7934598
fix(console): mobile viewport correctness — kill dead canvas, dvh, sa…
mabry1985 Jul 8, 2026
6361055
fix(console): dvh on mobile overlays + drop dead pre-DS mobile shell …
mabry1985 Jul 8, 2026
39c69c9
fix(console): Settings collapses to one column on phones (#1901)
mabry1985 Jul 8, 2026
10a3d46
feat(observability): fleet trace export → lab (the flywheel Observe, …
mabry1985 Jul 8, 2026
3e385ec
fix(docs): master-detail Docs reader on phones (#1902)
mabry1985 Jul 8, 2026
4c3e1b2
fix(console): 40px touch targets for icon buttons on phones (#1903)
mabry1985 Jul 8, 2026
9e430b3
feat(observability): daily rsync of fleet trace dumps → lab dataset d…
mabry1985 Jul 8, 2026
9fbe167
feat(observability): fleet-tracing config toggle + portable rig setup…
mabry1985 Jul 8, 2026
0c80a29
docs(changelog): add fleet-tracing (Observe seam #1897) to Unreleased…
mabry1985 Jul 8, 2026
cf12bd9
chore: release v0.97.0 (#1908)
mabry1985 Jul 8, 2026
820d2ce
docs(observability): document fleet trace export + redaction + per-ri…
mabry1985 Jul 8, 2026
5e357d6
fix(goals): kick + inject the goal on turn 1, and make goal turns hea…
mabry1985 Jul 8, 2026
a05314c
feat(marketing): redesign landing page with in-action examples (#1913)
mabry1985 Jul 8, 2026
87741ff
feat: autonomous operating model — goals · tasks · scheduling · watch…
mabry1985 Jul 8, 2026
f796950
feat(tasks): durable task→goal attribution (ADR 0079 P3c) (#1917)
mabry1985 Jul 8, 2026
3dc47da
docs: align goal/watch guides + ADR 0079 with the shipped operating m…
mabry1985 Jul 8, 2026
d3afa86
fix(console): auth dialog is a blocking modal; dialog scroll verified…
mabry1985 Jul 8, 2026
800c5b8
fix(delegation): remove task_output; bias fleet delegation to fire-an…
mabry1985 Jul 8, 2026
63ff4de
feat(marketing): roll Windows back to the notify-me gate (coming soon…
mabry1985 Jul 9, 2026
cd35599
feat(plugins): orgChart — live fleet delegation diagram (#1925)
mabry1985 Jul 10, 2026
676977b
docs: guide — safely exposing a protoAgent to the world (#1920)
mabry1985 Jul 10, 2026
cd63114
feat(tools): multimodal ToolMessage — an opt-in envelope lets a tool …
mabry1985 Jul 11, 2026
22aaf33
feat(gateway): expose a reusable gateway HTTP client for plugins (#19…
mabry1985 Jul 11, 2026
6e9533a
fix(e2e): work-overview met-today fixture flakes in the 10 min after …
mabry1985 Jul 11, 2026
3fecac2
feat(media): first-class media output channel — registry.save_media +…
mabry1985 Jul 11, 2026
e4c40ca
docs: upsert the media-platform trio (#1929/#1930/#1931) into the doc…
mabry1985 Jul 11, 2026
06441ed
fix(console): converge a settled turn to the canonical text + never r…
mabry1985 Jul 11, 2026
1e5dccd
docs(changelog): backfill [Unreleased] for the v0.98.0 batch (#1940)
mabry1985 Jul 11, 2026
eab92a4
chore: release v0.98.0 (#1941)
mabry1985 Jul 11, 2026
e8ad9a3
docs(marketing): refresh the stale roadmap against shipped reality (#…
mabry1985 Jul 11, 2026
15874a9
fix(console): absolutize server-relative media URLs + render multimod…
mabry1985 Jul 12, 2026
351a91b
fix(compat): accept OpenAI multimodal content lists on /v1/chat/compl…
mabry1985 Jul 12, 2026
fbd3482
fix(console): agent theme applies on first load + notch matches heade…
mabry1985 Jul 12, 2026
8cd0e22
feat(ci): roadmap staleness guard — flag Planned/In-progress refs to …
mabry1985 Jul 12, 2026
121c3f5
feat(console): render a waiting state on wait tool blocks (#1914) (#1…
mabry1985 Jul 12, 2026
9aee082
feat(plugins): optional tier for requires_pip — frozen installs warn …
mabry1985 Jul 12, 2026
91d1d57
chore: release v0.99.0 (#1956)
mabry1985 Jul 12, 2026
856a1b7
docs(plugins): consolidate the seam reference; fill gaps; fix drift (…
mabry1985 Jul 12, 2026
a07085a
fix(plugins): refresh the last boot-only plugin wiring on hot-reload …
mabry1985 Jul 12, 2026
481a57c
feat(plugins): reconcile plugin surfaces on config hot-reload (#1961)
mabry1985 Jul 12, 2026
af37276
fix(mcp): normalize discovered tool names to the documented <server>_…
mabry1985 Jul 12, 2026
632162c
chore: add CI/license/docs badges, CODE_OF_CONDUCT.md (#1964)
mabry1985 Jul 12, 2026
ba636ad
feat(secrets): pull env vars from external secrets managers — Infisic…
mabry1985 Jul 12, 2026
8892f7e
chore: track the GitHub social preview asset (#1966)
mabry1985 Jul 12, 2026
300fd05
feat(console): Settings ▸ Secrets panel for the secrets manager (ADR …
mabry1985 Jul 12, 2026
4ed009d
docs: backfill CHANGELOG [Unreleased] entries for #1958/#1959/#1961/#…
mabry1985 Jul 12, 2026
387fa80
chore: release v0.100.0 (#1968)
mabry1985 Jul 12, 2026
d9083bc
feat(chat): bridge image attachments into the media store so tools ca…
mabry1985 Jul 12, 2026
228a43e
feat(console): chat image action cluster + Lightbox — @protolabsai/ui…
mabry1985 Jul 12, 2026
88db0f7
chore: normalize package-lock with npm 11 + document the npm-10 works…
mabry1985 Jul 12, 2026
f511cac
perf(mcp): persistent session pool — one live session per server, reu…
mabry1985 Jul 12, 2026
4ff0ff3
fix(plugins): uninstall tears down live state — module purge, plugins…
mabry1985 Jul 12, 2026
1977bea
fix(console): HITL forms float above the chat instead of shifting it …
mabry1985 Jul 12, 2026
181cfc5
feat(console): favorite models + /model quick-switch inline picker (#…
mabry1985 Jul 12, 2026
2837d73
fix(console): HitlForm honors schema defaults + full keyboard operati…
mabry1985 Jul 12, 2026
1520dc8
chore: release v0.101.0 (#1980)
mabry1985 Jul 12, 2026
0dc3c86
fix(docker): pin npm 11 in the web-builder stage — npm 10 rejects the…
mabry1985 Jul 13, 2026
1ff5ab9
fix(chat): self-heal stalled turns so a large answer never spins "Wor…
mabry1985 Jul 14, 2026
1acf16c
fix(chat): stop currency amounts rendering as LaTeX math (#1983)
mabry1985 Jul 15, 2026
b30ecb7
build: make the wheel a working, PyPI-publishable runtime + console (…
mabry1985 Jul 15, 2026
3f6f7ed
ci(publish): use npm 11 so the PyPI console build survives a cold cac…
mabry1985 Jul 15, 2026
5d148d3
build: publish to PyPI as protolabs-agent (protoagent name is similar…
mabry1985 Jul 15, 2026
0b0fc55
feat(persona): guarded edit_soul tool — the agent can refine its own …
mabry1985 Jul 15, 2026
b15cc22
refactor(config): consolidate duplicate _deep_merge helper (#1991)
mabry1985 Jul 15, 2026
b0a8016
fix(console): quieter, consistent elevation on chat report cards + sy…
mabry1985 Jul 15, 2026
0a34bcd
feat(acp): surface registered acp.agents in the runtime + model picke…
mabry1985 Jul 15, 2026
a1f2c0a
docs(adr): ADR 0082 — per-chat ACP runtime (hot-swap a coding agent l…
mabry1985 Jul 15, 2026
5f7855a
refactor(console): consume DS --pl-shadow-card + currency guard, drop…
mabry1985 Jul 16, 2026
4df0d42
chore: release v0.102.0 (#1997)
mabry1985 Jul 16, 2026
f47662d
fix(notes): guard the shared note against lost updates; fail loudly w…
mabry1985 Jul 16, 2026
aad8327
fix(settings): a rejected save no longer leaves disk ahead of the run…
mabry1985 Jul 16, 2026
8e44990
feat(notes): render markdown as you type — CodeMirror live preview, n…
mabry1985 Jul 16, 2026
b5fc955
feat(archetypes): Cowork mode — knowledge-worker archetype (ADR 0083)…
mabry1985 Jul 16, 2026
06c8932
perf(server): gzip text/JSON responses (SSE-safe) — closes #2005 (#2015)
mabry1985 Jul 16, 2026
34eefa8
fix(settings): map the two core sections that silently defaulted to P…
mabry1985 Jul 16, 2026
ce5c72f
feat(setup): wizard-native post-install setup — one-click deps, work-…
mabry1985 Jul 16, 2026
604dd76
feat(archetypes): archetype preview — see skills/plugins/SOUL before …
mabry1985 Jul 16, 2026
50269b7
docs(adr): 0084 — computer use, an out-of-process driver and the fenc…
mabry1985 Jul 16, 2026
a3ddf89
docs(adr): correct 0084's tool surface against a real driver (0.8.3) …
mabry1985 Jul 16, 2026
8fedf51
docs(changelog): backfill Unreleased for the cowork/setup/notes/gzip …
mabry1985 Jul 17, 2026
7d5f97a
chore: release v0.103.0 (#2026)
mabry1985 Jul 17, 2026
f3259e4
fix(settings): scope the settings body override to its own dialog — c…
mabry1985 Jul 17, 2026
2ffd15e
chore: re-lock after the protolabs-agent rename + record QA-tier stag…
mabry1985 Jul 17, 2026
8711acd
fix(console): QuickSetting dialog honours depends_on (D4) (#2006)
mabry1985 Jul 17, 2026
f50b9ff
docs(changelog): record the two dialog fixes + the re-lock under [Unr…
mabry1985 Jul 17, 2026
4008ec7
chore: release v0.103.1 (#2029)
mabry1985 Jul 17, 2026
df42c5a
ci: re-lock during prepare-release, and fail when uv.lock drifts from…
mabry1985 Jul 17, 2026
04851ca
fix(console): adopt @protolabsai/ui 0.56 — overlays portal to <body> …
mabry1985 Jul 17, 2026
75d416d
fix(ci): drop `--offline` from prepare-release's re-lock — it broke t…
mabry1985 Jul 17, 2026
6a33b53
chore: release v0.103.2 (#2033)
mabry1985 Jul 17, 2026
26c9411
ci: pin uv to 0.11.29 — its lock output is version-dependent (#2034)
mabry1985 Jul 17, 2026
36f51e7
chore(console): rename Settings ▸ Integrations → Plugins (#2035)
mabry1985 Jul 18, 2026
0e556d6
feat(runtime): provision a managed Node.js runtime on demand (ADR 008…
mabry1985 Jul 18, 2026
6957861
feat(console): one-click Install Node runtime in the MCP panel (ADR 0…
mabry1985 Jul 18, 2026
3eb1704
feat(bundles): seedable MCP servers via a bundle `mcp:` field (ADR 00…
mabry1985 Jul 18, 2026
557c265
fix(console): drop per-row "open in new window" action from the fleet…
mabry1985 Jul 18, 2026
ee5a244
feat(fs): read-write-no-delete fence mode + gated delete_file (ADR 00…
mabry1985 Jul 18, 2026
6ee5da7
fix(console): show a processing dot on tabs with a background server …
mabry1985 Jul 18, 2026
3981e49
feat(a2a): migrate to protolabs-a2a 0.3.0 — URI-keyed extension metad…
mabry1985 Jul 18, 2026
24d90fa
feat(console): chat-first mobile shell + the touch layer mobile never…
mabry1985 Jul 18, 2026
1b69321
chore(console): adopt @protolabsai/ui 0.57 and retire the temporary t…
mabry1985 Jul 18, 2026
9e20ef7
feat: device pairing — scan a QR to add a phone, revoke it individual…
mabry1985 Jul 18, 2026
fa6e44c
docs(changelog): backfill [Unreleased] for everything since v0.103.2 …
mabry1985 Jul 18, 2026
7d1eb23
chore: release v0.104.0 (#2049)
mabry1985 Jul 18, 2026
e2c39cf
feat(pairing): offer to make a loopback-bound agent reachable (ADR 00…
mabry1985 Jul 19, 2026
c34fd58
chore: release v0.104.1 (#2051)
mabry1985 Jul 19, 2026
755811f
fix(pairing): bind 0.0.0.0, not the chosen address — v0.104.1 hung th…
mabry1985 Jul 19, 2026
893dd5c
chore: release v0.104.2 (#2053)
mabry1985 Jul 19, 2026
64e7169
fix(pairing): surface the minted token, and say where a token lives (…
mabry1985 Jul 19, 2026
39771a3
feat(desktop): the app authenticates its own webview (#2055) (#2056)
mabry1985 Jul 19, 2026
726a870
chore: release v0.104.3 (#2057)
mabry1985 Jul 19, 2026
8b0b00f
fix(pairing): decide from the server's token state, not the browser's…
mabry1985 Jul 19, 2026
52abe30
chore: release v0.104.4 (#2059)
mabry1985 Jul 19, 2026
4c85448
feat(sdk): start_goal_loop/stop_goal_loop return as watch-based sugar…
mabry1985 Jul 19, 2026
b99ff0c
fix(settings): gate Devices behind a developer flag, default off (#2062)
mabry1985 Jul 19, 2026
cbfff5d
feat(review): untrusted-data framing + ref discipline in the panel pr…
mabry1985 Jul 19, 2026
0d9f0e3
docs(adr): 0088 — per-finding inline review threads (Proposed) (#2064)
mabry1985 Jul 19, 2026
c4c4ea9
feat(review): port the open-swe review disciplines into the panel pro…
mabry1985 Jul 19, 2026
2428662
feat(review): existing-thread awareness — suppress overlaps with live…
mabry1985 Jul 19, 2026
414d7ea
docs(changelog): restore the dropped preflight fix + cover the review…
mabry1985 Jul 20, 2026
9cbb7e0
chore: release v0.104.5 (#2068)
mabry1985 Jul 20, 2026
d885450
fix(auth): land the CORS-preflight fix that #2062's squash dropped (#…
mabry1985 Jul 20, 2026
90202be
fix(infra): stop bare-env attribution regen from wiping licenses to U…
mabry1985 Jul 20, 2026
9205d5e
Merge remote-tracking branch 'upstream/main' into chore/sync-upstream…
mabry1985 Jul 20, 2026
bebce83
test: repin config-roundtrip golden to this fork's example values
mabry1985 Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
70 changes: 70 additions & 0 deletions .beads/issues.jsonl

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ node_modules/

# Seccomp profile — Docker reads this on the host at runtime
# (via docker-compose security_opt), NOT from inside the image
seccomp-profile.json
deploy/seccomp-profile.json

# OS junk
.DS_Store
Expand Down
9 changes: 1 addition & 8 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,13 @@ body:
description: Minimal steps, logs, or a stack trace that demonstrate it.
validations:
required: true
- type: textarea
id: expected-actual
attributes:
label: Expected vs. actual
description: What you expected to happen, and what actually happened.
validations:
required: true
- type: textarea
id: acceptance
attributes:
label: Acceptance
description: How we'll know it's fixed — verifiable criteria.
validations:
required: true
required: false
- type: input
id: env
attributes:
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ jobs:
with:
node-version: "20"
- name: Verify workspace config (.beads / .automaker / owned runners)
# Fork-friendly (#1534): the .beads/.automaker/owned-runner baseline is the
# protoLabs *fleet* standard, so it runs across the fleet (protoLabsAI/*) —
# keeping gina/protoTrader/roxy conformant — but is skipped on external forks,
# which are free to diverge. The job stays green either way (the server.py
# guard below still runs everywhere).
if: startsWith(github.repository, 'protoLabsAI/')
# The npm package is still 1.0.0 (predates verify-workspace-config),
# so run the script directly. It's zero-dependency (node builtins
# only), so no `npm install` is needed. Switch to
Expand Down Expand Up @@ -83,6 +89,35 @@ jobs:
run: |
pip install import-linter==2.11
lint-imports
- name: Third-party attribution in sync
# THIRD_PARTY_LICENSES.md is generated from uv.lock + package-lock.json
# by scripts/gen_attribution.py. This gate re-derives the dependency
# inventory from those committed lockfiles and fails if the manifest
# drifted — i.e. someone bumped a dep without regenerating attribution.
# Reads only committed files (stdlib-only, no `uv sync` / `npm ci`), so
# it's fast and can't flake on platform differences. To fix a failure:
# `uv sync && uv run python scripts/gen_attribution.py`.
run: python scripts/gen_attribution.py --check
- uses: astral-sh/setup-uv@v6
with:
# Pinned like ruff/import-linter above, and for a sharper reason: uv's lock
# OUTPUT is version-dependent. 0.11.29 and 0.11.13 disagree about how to record
# dependency markers (0.11.29 drops `python_full_version < '3.13'` from aiologic
# /culsans), so an unpinned `latest` rewrites uv.lock whenever uv ships — the
# file then thrashes based on whose uv ran last, and this gate is the thing that
# notices. 0.11.29 produced the lock currently on main.
# To bump: change BOTH here and in prepare-release.yml (they must match), then
# run `uv lock` once and commit the result.
version: "0.11.29"
- name: Lockfile in sync with pyproject
# uv.lock silently rotted for a full release cycle (#1998): it kept a
# `git+https` ref for protolabs-a2a after pyproject moved to the PyPI range,
# and — because that project's 0.2.0/0.2.1 ship different APIs — any
# `--frozen`/`--locked` install pulled a build the runtime can't call. Nothing
# caught it: the CI jobs install with plain `pip`, so uv.lock is only read by
# the attribution gate above (which tolerates a stale project version).
# Fails when pyproject.toml and uv.lock disagree. To fix: `uv lock`.
run: uv lock --check

tests:
name: Python tests
Expand Down Expand Up @@ -176,6 +211,11 @@ jobs:
with:
node-version: "20"
cache: npm
# The lockfile is npm-11-shaped (PROTO.md: npm-10 arborist no-ops workspace
# dep bumps and disagrees with 11 about peer-stub reachability, so its `ci`
# rejects an 11-generated lock). Pin every root `npm ci`/`install` to 11.
- name: Use npm 11
run: npm install -g npm@11
- name: Install dependencies
run: npm ci
- name: Run unit tests (vitest)
Expand Down
102 changes: 59 additions & 43 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,35 @@ name: Desktop Build
# updater bundles, and the updater-manifest fan-in job composes latest.json
# (the manifest the in-app updater polls) onto the release — uploaded last.
#
# Fires on EVERY semver tag push (patch included) alongside release.yml's Docker build,
# and on manual dispatch. Patches used to be skipped to save CI minutes, but this repo is
# public so GitHub-hosted runners (incl. the 10×/2× macOS/Windows legs) are free +
# unlimited — so every release, patch or minor, ships fresh desktop binaries and an
# updated latest.json (no more hand-attaching a patch's binaries). macOS builds are signed
# only when the full Apple secret set is present; Linux/Windows are always unsigned. See
# ADR 0010 (UI tiers / desktop).
# Runs on MANUAL DISPATCH ONLY (workflow_dispatch) — deliberately NOT on tag pushes.
# The macOS (10×) and Windows (2×) legs are this repo's only paid CI, and building the
# full matrix on EVERY semver tag (dozens/month at current release cadence) was the
# dominant cost — so desktop drops are now on-demand instead of per-release. Tag pushes
# still ship the Docker image + a (non-Latest) GitHub Release via release.yml; only the
# desktop binaries wait for a dispatch.
#
# Two dispatch modes, keyed off the `tag` input:
# • WITH a `tag` (a vX.Y.Z) → a real desktop RELEASE: binaries attach to that GitHub
# Release, the fan-in composes latest.json, and the release is promoted to 'Latest'
# (release.yml creates releases --latest=false; the promotion happens here). This is
# how a desktop update reaches users' in-app updater.
# • WITHOUT a tag (dispatched from a branch) → a TEST build: bundles upload as workflow
# artifacts only; no release, no latest.json, no 'Latest' change.
#
# macOS builds are signed only when the full Apple secret set is present; Linux/Windows
# are always unsigned. The repo guard keeps forks from running the matrix. See ADR 0010
# (UI tiers / desktop) and docs/guides/releasing.md § Desktop.

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Tag/ref to build against (blank = current ref)'
description: 'Tag to publish a desktop release for (vX.Y.Z). Blank = test build (artifacts only, no release).'
required: false

concurrency:
group: desktop-build-${{ github.ref }}
# Keyed on the dispatched tag (or ref) so two release dispatches don't collide.
group: desktop-build-${{ inputs.tag || github.ref }}
cancel-in-progress: false

permissions:
Expand All @@ -47,12 +56,11 @@ permissions:
jobs:
build:
name: ${{ matrix.target }}
# Build on every semver tag push (patch included) + any manual dispatch. The old
# patch-skip was a CI-cost guard; it's unnecessary on a public repo where GH-hosted
# runners are free, and it meant patch desktop fixes never reached users (their
# binaries weren't attached and latest.json kept pointing at the last minor). The
# repo guard keeps forks from running the matrix.
if: github.repository == 'protoLabsAI/protoAgent'
# Manual dispatch only (see the `on:` block) — the tag-push trigger was retired
# because the macOS/Windows matrix on every release was the repo's dominant CI cost.
# Fork-friendly (#1534): a fork can dispatch its own (unsigned) build; the repo
# clause keeps forks from auto-building should a tag trigger ever return.
if: github.repository == 'protoLabsAI/protoAgent' || github.event_name == 'workflow_dispatch'
# Per-leg runner, overridable by repo variable so the expensive macOS/Windows
# legs can move off GitHub-hosted runners (10×/2× billing multipliers) onto
# Namespace profiles WITHOUT editing this file — the moment the org provisions
Expand Down Expand Up @@ -143,6 +151,7 @@ jobs:
# desktop. Generate it now (into public/_ds) so build_sidecar can bundle it. The
# root install also wires the @protoagent/web workspace for the tauri build below.
run: |
npm install -g npm@11 # npm-11-shaped lockfile — npm 10's `ci` rejects it (see PROTO.md)
npm ci
node apps/web/scripts/copy-plugin-kit.mjs
ls -la apps/web/public/_ds/
Expand Down Expand Up @@ -199,8 +208,8 @@ jobs:
SIGNED="unsigned (dev)"
if [ "${RUNNER_OS}" = "macOS" ]; then
# tauri-bundler signs as soon as *any* Apple secret is set, then fails
# if the set is incomplete. Require the full set; otherwise unset all so
# a dispatch build falls back cleanly to unsigned.
# if the set is incomplete. Require the full set for a tagged (publish)
# dispatch; a test build (no tag) unsets all and falls back to unsigned.
if [ -n "${APPLE_CERTIFICATE}" ] && [ -n "${APPLE_CERTIFICATE_PASSWORD}" ] \
&& [ -n "${APPLE_SIGNING_IDENTITY}" ] && [ -n "${APPLE_API_ISSUER}" ] \
&& [ -n "${APPLE_API_KEY}" ] && [ -n "${APPLE_API_KEY_BASE64}" ] \
Expand All @@ -210,8 +219,8 @@ jobs:
python3 -c 'import base64,os,sys; open(sys.argv[1],"wb").write(base64.b64decode(os.environ["APPLE_API_KEY_BASE64"]))' "$KEYFILE"
export APPLE_API_KEY_PATH="${KEYFILE}"
SIGNED="Developer ID (signed + notarized)"
elif [ "${{ github.event_name }}" = "push" ]; then
echo "::error::Semver-tag desktop releases require the full Apple signing secret set."
elif [ -n "${{ inputs.tag }}" ]; then
echo "::error::A tagged desktop release (tag=${{ inputs.tag }}) requires the full Apple signing secret set."
exit 2
else
unset APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY \
Expand Down Expand Up @@ -335,45 +344,45 @@ jobs:
if [ "${{ steps.build.outputs.signed }}" = "true" ]; then FLAG="--require-signed"; fi
scripts/verify-macos-desktop.sh $FLAG dist-desktop/*.dmg

- name: Upload dispatch artifact
if: github.event_name == 'workflow_dispatch'
- name: Upload test-build artifact
# No tag → a test build: bundles are downloadable workflow artifacts only.
if: inputs.tag == ''
uses: actions/upload-artifact@v6
with:
name: protoAgent-${{ steps.version.outputs.version }}-${{ matrix.target }}
path: dist-desktop/*

- name: Attach to release
if: github.event_name == 'push'
# A tag → publish: attach binaries to that GitHub Release (fan-in adds latest.json).
if: inputs.tag != ''
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: gh release upload "${{ steps.version.outputs.tag }}" dist-desktop/* --clobber

# Fan-in: compose latest.json from every leg's signed updater bundle and
# upload it LAST, so the manifest never points at assets that don't exist
# yet. Runs only on semver tags, and only once ALL legs succeeded (a partial
# manifest would skew versions across platforms). If the release carries no
# updater bundles (key wasn't present), it skips — in-app updates are simply
# disabled for that release.
# yet. Runs only for a tagged (publish) dispatch, and only once ALL legs
# succeeded (a partial manifest would skew versions across platforms). If the
# release carries no updater bundles (key wasn't present), it skips — in-app
# updates are simply disabled for that release.
updater-manifest:
name: updater manifest (latest.json)
needs: build
# Runs on every tag push (patch included) so the in-app updater's latest.json is
# refreshed for every release. Gated to `push` so a manual dispatch (which builds
# artifacts, with no release to attach to) doesn't try to compose a manifest;
# `needs: build` ties it to a successful matrix.
# Only for a publish dispatch (a `tag` was supplied) — a test build (no tag) has no
# release to attach a manifest to. `needs: build` ties it to a successful matrix.
if: >-
github.event_name == 'push' && github.repository == 'protoLabsAI/protoAgent'
inputs.tag != '' && github.repository == 'protoLabsAI/protoAgent'
runs-on: ubuntu-latest # workspace-config: allow-hosted-runner public repo — GH-hosted is free
timeout-minutes: 10
steps:
# Checkout at the tag so we can read the rolled CHANGELOG.md section for the
# in-app updater notes (below). By tag-push time CHANGELOG.md already holds the
# in-app updater notes (below). By publish time CHANGELOG.md already holds the
# dated `## [VERSION]` section — the bump PR (prepare-release.yml) merged before
# the tag. Without this the job has no working tree (it only `gh release`s).
- uses: actions/checkout@v5
with:
ref: ${{ github.ref_name }}
ref: ${{ inputs.tag }}

- name: Setup Node
uses: actions/setup-node@v5
Expand All @@ -386,7 +395,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
TAG="${{ github.ref_name }}"
TAG="${{ inputs.tag }}"
VERSION="${TAG#v}"
mkdir -p updater-dist
gh release download "$TAG" --repo "${{ github.repository }}" --dir updater-dist \
Expand All @@ -397,12 +406,19 @@ jobs:
exit 0
fi
# Updater notes, best → worst source:
# 1. The curated CHANGELOG.md section for this version (the human-written
# "### Added/Changed/Fixed" markdown) — what the in-app UpdateNotice renders.
# 2. The GitHub Release body (auto-generated commit subjects) — used when the
# CHANGELOG section is empty (e.g. a patch with no [Unreleased] bullets).
# 3. A generic placeholder, if both are empty.
NOTES="$(python3 scripts/changelog.py notes "$VERSION" 2>/dev/null || true)"
# 1. The LLM-themed notes posted to Discord — release.yml uploads them as the
# `release-notes.md` asset, so the in-app UpdateNotice renders the SAME text as
# the Discord embed (one changelog, one voice — #1516).
# 2. The curated CHANGELOG.md section for this version (the human-written
# "### Added/Changed/Fixed" markdown) — used when the LLM step didn't run
# (e.g. a fork with no gateway key, or the Discord step failed).
# 3. The GitHub Release body (auto-generated commit subjects) — if both are empty.
# 4. A generic placeholder, if all are empty.
NOTES=""
if gh release download "$TAG" --repo "${{ github.repository }}" -p release-notes.md -D "$RUNNER_TEMP" --clobber 2>/dev/null; then
NOTES="$(cat "$RUNNER_TEMP/release-notes.md" 2>/dev/null || true)"
fi
[ -z "$NOTES" ] && NOTES="$(python3 scripts/changelog.py notes "$VERSION" 2>/dev/null || true)"
[ -z "$NOTES" ] && NOTES="$(gh release view "$TAG" --repo "${{ github.repository }}" --json body -q .body 2>/dev/null || true)"
[ -z "$NOTES" ] && NOTES="protoAgent ${TAG} — see the GitHub Release for details."
npx --yes -p '@protolabsai/release-tools@latest' build-updater-manifest \
Expand Down
13 changes: 9 additions & 4 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@ permissions:
env:
REGISTRY: ghcr.io
# Normalise the image name — GHCR paths are case-sensitive and Docker refuses
# uppercase; github.repository would otherwise yield a mixed-case value.
IMAGE_NAME: protolabsai/leadengineer
# uppercase; github.repository would otherwise yield a mixed-case value. A fork
# that opts in (see the job guard) sets its own `IMAGE_NAME` repo variable.
IMAGE_NAME: ${{ vars.IMAGE_NAME || 'protolabsai/protoagent' }}

jobs:
build-and-push:
# Fork-friendly (#1534): the hardcoded default pushes to protoLabs' GHCR, which
# forks can't write to. Only the canonical repo runs by default; a fork opts in
# by setting the `DOCKER_PUBLISH_ENABLED` repo variable (+ its own `IMAGE_NAME`).
if: github.repository == 'protoLabsAI/protoAgent' || vars.DOCKER_PUBLISH_ENABLED == 'true'
runs-on: ubuntu-latest # workspace-config: allow-hosted-runner public repo — GH-hosted is free
timeout-minutes: 30

Expand All @@ -55,8 +60,8 @@ jobs:
type=raw,value=latest
type=sha,prefix=sha-,format=short
labels: |
org.opencontainers.image.title=leadengineer
org.opencontainers.image.description=leadEngineerprotoAgent Lead Engineer team bundle
org.opencontainers.image.title=protoagent
org.opencontainers.image.description=protoAgentLangGraph + A2A template
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}

Expand Down
13 changes: 10 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ on:
# PR-level gate (#976): vitepress fails the build on parse errors AND dead links,
# so running docs:build here catches doc breakage at review time instead of letting
# it merge green and only go red on the main-only deploy. Build-only — no deploy.
# Unfiltered on purpose: `build` is a REQUIRED check in the main ruleset, and a
# path-filtered required check never reports on non-matching PRs, blocking them
# forever. Running everywhere (~30s) is the price of making red docs unmergeable
# (a dead link once automerged and froze the Pages deploy on every main push).
pull_request:
paths: ['docs/**', 'package.json', 'package-lock.json', '.github/workflows/docs.yml']
workflow_dispatch:

permissions:
Expand All @@ -31,6 +34,7 @@ jobs:
with:
node-version: 20
cache: npm
- run: npm install -g npm@11 # keep resolution npm-11-consistent with the lockfile (see PROTO.md)
- run: npm install
- run: npm run docs:build
- uses: actions/upload-pages-artifact@v5
Expand All @@ -41,8 +45,11 @@ jobs:

deploy:
needs: build
# Never deploy from a PR — PRs only validate the build above.
if: github.event_name != 'pull_request'
# Never deploy from a PR — PRs only validate the build above. Fork-friendly
# (#1534): deploys to protoLabs' GitHub Pages, so only the canonical repo runs
# it — a fork without Pages configured would otherwise get a deploy error. The
# `build` job above is unguarded on purpose: forks want it (catches VitePress breakage).
if: github.event_name != 'pull_request' && github.repository == 'protoLabsAI/protoAgent'
runs-on: ubuntu-latest # workspace-config: allow-hosted-runner public repo — GH-hosted is free
environment:
name: github-pages
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/issue-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ jobs:

const text = body.replace(/\s+/g, ' ').trim();
const longEnough = text.length >= 80;
const hasProblem = hasSection(/problem|what'?s? wrong|motivation|background|context|summary/i);
const hasRepro = hasSection(/repro|reproduce|steps|evidence|expected|actual|observed/i);
const hasProposal = hasSection(/propos|solution|approach|direction|fix|design/i);
const hasProblem = hasSection(/problem|what'?s? wrong|motivation|background|context|summary|observed|symptom|idea|current behavior|\bwhat\b/i);
const hasRepro = hasSection(/repro|reproduce|steps|evidence|expected|actual|observed|symptom|root cause/i);
const hasProposal = hasSection(/propos|solution|approach|direction|fix|design|\bwork\b|plan/i);
const hasAcceptance = hasSection(/acceptance|done when|success criteria|definition of done/i);

const missing = [];
Expand Down
Loading
Loading