v0.3.0
v0.3.0
The provider-aware second-brain launcher: the web app's Today panel can now
take you to your second brain — one honest action, one explicit click,
nothing automatic. Under it sits one launch-policy owner, one atomic
configuration-mutation owner, and release evidence that exercises the launcher
from the installed wheel rather than the source checkout.
StudyLoop 0.3.0 follows the documented source-checkout installation flow. CI
builds and installs wheel/sdist artifacts as validation evidence; this release
does not advertise GitHub or PyPI binary distribution.
Changes
- Today launches your second brain. A read-only
GET /api/second-brain/launch-targetroute (never cached) reports the
selected provider's honest launch state; the Today panel renders at most one
launcher action from prefetched state and navigates only inside the click
handler. xTiles opens once in a newnoopener,noreferrertab; Obsidian
hands the current tab to theobsidian://link without leaving an empty tab
behind. Nothing navigates during page load, refresh, publication or
wind-down, and no server module launches, redirects, or contacts a provider. - Obsidian is same-device honest. The action is enabled only when the
browser runs on the device running StudyLoop, decided from the direct
request peer — behind a reverse proxy it stays safely disabled with the
reason. An enabled click opens<vault>/<folder>/Today.mdwhen that note
exists inside the vault, falling back to the vault root. - The assistant destination handoff.
studyloop brain destination set --provider xtiles --url URLretains a reviewed, connector-returned page URL
without changing provider consent;destination clearremoves it. The
validator accepts HTTPS on exactlyxtiles.app/app.xtiles.appwith a real
page path and nothing else — no userinfo, port, query, or fragment, Unicode
and IDNA lookalikes rejected — and a rejected value is reported by reason
only, never echoed. Only the host is ever shown; Settings never displays the
full retained URL. - One atomic configuration writer.
mutate_raw_config()is now the only
read-modify-write seam: exclusive sibling lock, reread after locking,
whole-config validation, synced0600temporary sibling, atomic replace
preserving the destination's mode.brain enableand both destination
commands share it, so concurrent StudyLoop writers cannot silently lose each
other's update. - Settings explains, never launches. One card per provider — active for
the selected provider, muted otherwise — carrying the launch API's own
reason as guidance, or the CLI command that would select a muted provider.
No web form writes configuration. - Installed-wheel launcher evidence.
just smoke-web(wired into
just release-check) builds the wheel, installsstudyloop[web]into an
isolated environment outside the checkout, asserts the installed package
does not import from the checkout, starts the installed application,
requests the launch-target route, and loads every launcher asset reachable
frommain.js's import graph. - Session memory became an installer/doctor invariant across all five
harnesses, topic exercises moved behind an explicit--devpreview flag,
and release validation now runs without the three warning classes found
during the 0.3.0 readiness pass (see CHANGELOG for detail).
Verification
just preflight— 4882 passed, 4 skipped (ruff clean, Pyright 0 errors,
JS 105/105, docs build strict, release consistency and spec validation pass)just e2e— 515 passed, 20 skippedjust smoke-web— 3 passed (installed-wheel launcher smoke: isolated venv
outside the checkout, launch-target route, full launcher asset graph)just smoke-installed,just smoke-extras(9 passed),just shellcheck,
just audit,just audit-full— all pass
Connector-shape evidence (redacted)
A live page URL returned by the authenticated xTiles MCP connector on
2026-09-05 was checked against the strict destination validator. The URL
itself is deliberately not recorded here — only its shape:
- scheme
https, host exactlyxtiles.app, default port - no userinfo, no query, no fragment, ASCII-only, well under 2,048 characters
- path is a single opaque page-id segment (non-home)
resolve_second_brain() accepted that exact value unchanged (provider
selection untouched), and rejected the same value with a query appended
and with a fragment appended, each with a reason-only error that does not echo
the value — confirming the strict validator matches what the connector really
returns, with no contract revision needed.
Tag status
v0.3.0 is cut at the release commit 34f06d70 (2026-09-06, the commit that bumped the
version and added this note). The tag was created on 2026-09-14 during the congruence
review, once the release gate had learned to run before a tag exists; the CHANGELOG heading
carries the tag's commit date.