353 Commits
Author SHA1 Message Date
jason.woltje d68e7b0976 Fix recovery review findings: live Console identity, crashed clear fence, atomic quarantine (P19 #857, #862)
- A Console is stale only when its pid is gone or its start time differs. A live Console
  started from another entry path is never quarantined; stop refuses to signal it and
  names the path it was started from.
- The lock-clear fence records the clearer's pid and start time. A fence left by a dead
  clearer is moved aside and recovery continues; a live or unreadable fence refuses.
  lock show reports a fence.
- Console state is moved aside with one atomic rename after an inode re-check.
2026-11-08 14:52:42 -06:00
jason.woltje e9a7adab11 Recover stale Console state, dead org locks and wedged bus hosts
console start and stop move a console.json whose recorded process is gone
aside instead of refusing (P19 #857). mosaic org lock show|clear reports a
lifecycle lock holder and clears it only when the holder is dead.
mosaic bus recover marks a bus host stopped when its process is gone and its
socket does not answer, and writes a recovery receipt (P19 #862).
2026-11-08 14:11:13 -06:00
jason.woltje 606911834e Merge organization archive and fixes (quick-setup 57d5675ae)
# Conflicts:
#	packages/cli/src/cli.mjs
2026-11-07 23:06:53 -06:00
jason.woltje 312c62fd4f Fence organization archive against in-flight startup 2026-11-07 23:05:43 -06:00
jason.woltje ec143b2b9c Add organization archive and restore 2026-11-07 22:49:49 -06:00
jason.woltje 009024ea45 Merge organization quick setup and delete (quick-setup 614559a94) 2026-11-07 22:37:13 -06:00
jason.woltje 047825460f Delete an organization after two confirmations
mosaic org delete ID --confirm ID --if-revision N, and POST /api/orgs/:id/delete,
mark the organization as deleting, stop its bus, move its files into
<dataRoot>/deleted/<ID>-<stamp>/ with a receipt, then drop it from the registry
and append deleted.log. A failure stops at its step and a rerun resumes. Bus
start refuses an organization that is being deleted. The settings page asks
twice: a panel that lists the effects, then typing the ID before Delete
permanently is enabled. Purge and remote revocation are follow-ups.
2026-11-07 22:22:05 -06:00
jason.woltje 3272fd9c6f Let the import scanner accept the plug-in loader's own module import
Quick Setup made organization code import the plug-in loader, which loads
packages/plugins/<kind>/<name>/index.mjs by a computed path. The scanner now
allows exactly that form in the loader file and still refuses it anywhere else.
2026-11-07 22:22:05 -06:00
jason.woltje 632a4f16b9 Serve the dev fixture only to organizations that use it
The dev fixture guard was instance-wide: once a fixture record existed,
every organization's bus start refused when a provider credential was in
the environment. One instance could not hold a fixture organization and a
real-model organization side by side.

Bus start now reads the organization file. An organization that does not
route a launch to the fixture launches as configured. A fixture
organization's launch environment drops provider credentials before the
guard runs.
2026-11-07 21:22:13 -06:00
jason.woltje 8f800c1361 Verify shared proxy and organization integration 2026-11-07 13:50:39 -06:00
jason.woltje 62261ca1fc Update tester fixtures for organization transport 2026-11-07 13:18:34 -06:00
jason.woltje ea46b21a5d Update pinned copy fixture for organization buses 2026-11-07 13:17:09 -06:00
jason.woltje 6feb8a68bb Update two-instance bus fixture for organizations 2026-11-07 13:15:52 -06:00
jason.woltje 8dd9b37fe0 Expose organization boot status 2026-11-07 13:07:43 -06:00
jason.woltje f819c55302 Use organization roots in channel proof fixtures 2026-11-07 12:49:24 -06:00
jason.woltje 9750609f4c Restore scoped dev-owner launch checks 2026-11-07 12:45:13 -06:00
jason.woltje ed081626b5 Bind organization identity into bus boot 2026-11-07 12:36:27 -06:00
jason.woltje 12997c78c8 Merge commit 'b52adc841' into ui-org
# Conflicts:
#	packages/cli/src/cli.mjs
#	scripts/mosaic
2026-11-07 12:31:16 -06:00
jason.woltje 4b8bbc06a0 Merge commit '2d8522887' into ui-org 2026-11-07 12:29:14 -06:00
jason.woltje d9d1af2e0f Merge commit '24e240d44' into ui-org
# Conflicts:
#	scripts/mosaic
2026-11-07 12:28:36 -06:00
jason.woltje 2d8522887c Protect shared proxy organization keys 2026-11-07 11:42:56 -06:00
jason.woltje 5dcc5bf77c Add mosaic run evidence
Read-only: finds a thread's launch and end records in the organization launch log and checks each run's session.json, bundle manifest digests and turn stderr files.
2026-11-07 11:30:44 -06:00
jason.woltje 3723cc56ec Add mosaic dev fixture init, status and stop
Starts the fixture provider's loopback endpoint for a named dev instance and records it under the data root. Refuses the default instance, a configured proxy, provider credentials in the environment, a non-loopback bind and a second fixture. fixtureLauncherOptions hands the endpoint to the launcher as its session proxy context.
2026-11-07 11:30:44 -06:00
jason.woltje 24e240d44e Scope buses to organization roots 2026-11-07 11:29:13 -06:00
jason.woltje 3456d93af0 Implement organization setup commands 2026-11-07 11:17:31 -06:00
jason.woltje 488431976e Keep launch recovery evidence when broker end is unconfirmed 2026-11-06 23:12:02 -06:00
jason.woltje b48396b68e fix(plugins): provider review fixes, admission binding, alias-safe models, no provider names in core (#1858) 2026-11-06 19:52:57 -06:00
jason.woltje ccccac2ade feat(plugins): model providers as plugin modules, cliproxy catalogue on Routing (#1858) 2026-11-06 19:11:13 -06:00
jason.woltje 26f1218b9c fix(bus): retry chief after processing resumes 2026-11-06 06:51:39 -06:00
jason.woltje aa88548d64 Merge feat/ui797-stall-guard-int (8c568ea46) into integrate/mvp3-ux-1106
# Conflicts:
#	BUILD-LOG.md
#	docs/SESSIONS.md
2026-11-06 05:03:42 -06:00
jason.woltje f23a998eed fix: close project archive admission races (#797) 2026-11-06 02:54:04 -06:00
jason.woltje c7ddc7d31c fix(harness): mark first streamed model output (#797) 2026-11-06 02:02:43 -06:00
jason.woltje 1c33c342bc fix(archive): fence new execution and preserve archived reads (#797) 2026-11-06 01:51:08 -06:00
jason.woltje 3e21d239b3 feat(bus): owner project archive and unarchive (#797)
Jason's owner report 2026-11-06: no way to archive or delete projects.
Archive is the reversible half; delete stays out of scope because project
and run records are write-once evidence.

- project.archive / project.unarchive: human-only (project.create's gate
  minus the tester carve-out; tester, user and agent refused). State is the
  latest row of a new append-only project_archives journal (3s->3t, additive;
  the rows are the audit trail), so projects itself never changes and nothing
  is deleted. Repeats refuse project-already-archived / project-not-archived.
- project.list hides archived projects until {includeArchived:true}, which
  flags each with archived:true; a user still lists granted projects only.
- While archived, thread.create, thread.send, room.open and room.send refuse
  project-archived; every existing record stays readable and unarchive
  restores everything.
- Console: new public/project-archive.js (+css) and project-archive-http.mjs
  routes (chatHTTP's write fence); chat.js gains two one-line hooks,
  index.html one script and one link tag; styling only through the shell's
  .project-archive-control / .project-archived-filter hooks.
- CLI: mosaic project archive|unarchive <name-or-id> and list --archived.
- stack-release COUNTED/LISTED gain project_archives (schema order); schema
  step-count pins and migration fixtures updated for 3t.

bus 1601/1601 (Node 24; Node 26 has the pre-existing project-file socket
stall), webui 582/582, plugins 132/132, cli 1191/1194 (two pre-existing
load flakes of the row253 C4 and land_work families; both pass isolated and
the base commit's full suite fails four of the same family).
2026-11-05 23:50:51 -06:00
jason.woltje 51525a31d6 test(lifecycle): cover three delivery paths and crash recovery (#1839) 2026-11-04 14:47:45 -06:00
jason.woltje f7378f365c fix(release): read inline migration pairs in the release chain reader (#811) 2026-11-04 13:20:11 -06:00
jason.woltje 647626bdd1 feat(bus): resume product requests through typed role continuations (#1839) 2026-11-04 12:18:10 -06:00
jason.woltje b5a58b48b4 feat(tasks): bind requirement and git admission to product scope (#1839) 2026-11-04 11:24:39 -06:00
jason.woltje 793f3b157d fix(cli): upgrade and roll back Case 2 proxy hubs (#772) 2026-11-04 11:07:57 -06:00
jason.woltje 3b071cac7b feat(bus): persist pre-task request intake and selection (#1839) 2026-11-03 17:53:45 -06:00
jason.woltje 18a0bd47d7 Merge Console writes: factory Console takes the paired --sso --send; .53 launches through the proxy (Vikunja #415) 2026-11-02 22:16:25 -06:00
jason.woltje bd79daffdd fix(case2-unit): factory Console takes the paired --sso --send; .53 launches through the proxy (Vikunja #415, #416)
The factory profile of scripts/case2-unit.sh now takes the bare pair
--sso --send, ported from the lane's serve_flags rule (b00019548). Either
flag alone refuses, and the proxy preview set and the proof pair still
refuse. Before any unit write, a bounded query run as the stack user,
through the existing config-query supervisor, checks that the stack
user's system config has console.sso; the Console refuses --sso without
it at start (packages/webui/src/cli.mjs:95). The server needs no change.

The library profile is unchanged: still read-only, still refusing --send
and --sso. Taking library --send with forward-auth and proof would need
the #385 refusal reverted in cli.mjs:63, serve.mjs:681 and the service,
release and upgrade scripts, so it is not done here.

docs/guides/case1-business.mosaic-stack.json now launches
claude-opus-5-5 with provider proxy, route anthropic, under account
factory with credential proxy.
2026-11-02 22:09:01 -06:00
jason.woltje b9811c492f Merge #774 R2: the first proxy check gates the route the final check picks (Vikunja #774) 2026-11-02 21:52:46 -06:00
jason.woltje b8f01a189f fix(row 332 R2): proxy first check gates the route the final check picks (Vikunja #774)
Route selection moves into one helper, pickRoute(proxyOf), that both checks call: the
primary preflight, and the fallback with its own preflight only when the primary's
fails, with the same messages. The final check passes proxyFor and is otherwise
unchanged. The first check of a proxy launch passes a stand-in session proxy (marker
key PENDING_ADMISSION_KEY and the configured loopback base URL), which reaches only
the preflights, then runs gate() with a null session proxy on the predicted route only.
The round 1 union over both routes is gone.

The managed fixture takes extra (more harnesses switched on), real (harnesses loaded
unfaked) and hostEnv. Seven tests in proxy-launch-order.test.mjs.
2026-11-02 21:37:48 -06:00
jason.woltje 180fb8ab00 fix(row 333 follow-ups R3): a spread before import is a dynamic import (Vikunja #779)
The negative lookbehind in DYNAMIC and in the dynamic branch of IMPORT excluded any dot
before `import`. That also skipped the last dot of the spread punctuator, so
`${({...import('../x.mjs')})}` returned [] for an existing, a missing and an outside
target, and `{...import(name)}` in plain code was neither returned nor refused.

Both patterns now share one fragment: a word character or $ before `import`, or a single
dot (x.import, x?.import), makes it a property call; a dot that follows another dot ends a
spread and does not exclude it. `1..import(` is read as an import too, which errs toward
refusal; the header names it.

Tests: a spread inside a substitution refuses for an existing, a missing and an outside
target and for array spread; a spread in plain code is returned, reaches the target
checks, and is refused when its argument is not a plain string; x.import and x?.import
stay property calls in plain code and in a substitution.
2026-11-02 21:13:56 -06:00
jason.woltje d3b6ab11da test(row 333 follow-ups R2): refuse a dynamic import inside a template substitution, read division after ++ and -- (Vikunja #779)
The business-imports helper blanked a whole template literal, so a dynamic import inside a
${...} substitution was neither found nor refused and skipped the existence and containment
checks. The scanner now reads a substitution to its closing brace, refuses it with
"unsupported import inside a template substitution" when it holds a dynamic import, and drops
it otherwise, so a nested template and a brace inside a string no longer end it early.

A slash after a postfix ++ or -- was read as the start of a regular expression, so
`n++ / 2` threw "unterminated regular expression". After a ++ or -- pair that follows an
operand the slash is division; after one that follows an operator it still starts a regular
expression. The header names the rare form this still misreads.

Output on the real tree is byte-identical.
2026-11-02 20:59:35 -06:00
jason.woltje 85ecdfbde5 fix(row 332): proxy first check runs every key-independent refusal; testProxy test option (Vikunja #774)
F1: with system.proxy set, the first launcher check no longer returns
before the account, sandbox, capacity and limit checks. It skips only the
harness preflight and the session proxy key match, which need the key P2
admits in startLocked. A launch that can never start now refuses before
authorizeLaunch, the workspace and checkouts, and modelPreflight. The first
proxy pass accepts only credential "proxy" for a declared account, tries
the fallback model when the primary refuses, and lets mayLaunch
key-quota-no-model-key through so P2 can issue the first key. The final
check is unchanged and still runs before newRun and spawn.

F4: the proxy test option is renamed testProxy and createLauncher throws
test-proxy-refused when it is given with system.proxy.
launch-account.test.mjs is a second caller, so the option stays.

Tests: proxy-launch-order.test.mjs (6), launcher.test.mjs F4 throw test;
the managed launch fixture gains bwrap, admit and trace.ops.
2026-11-02 20:51:51 -06:00
jason.woltje 5fbf3d2b51 test(row 333): hold the port a refusal test uses instead of probing and closing it (Vikunja #782)
control-board serve.test: the non-loopback refusal test took a port from a
probe-and-close helper and then called startServer on it. It now binds its
own listener, keeps it for the whole test, and closes it in finally, so the
port cannot be taken by another process in between.

stack-release.test T-I4: the free port goes only to webui-service.sh install
rows that refuse before any listen, so it is now a closed lease
(leaseClosedPort), which refuses connects and blocks other binds.

The 22 other freePort callers hand the number to `stack-release.sh rehearse`,
which probes it and starts a Console later. That needs a script change and
is left as a design, with a comment above freePort naming the window.
2026-11-02 20:22:14 -06:00
jason.woltje 0e7e203db8 test(row 333): harden the business-imports helper and test it on small trees (Vikunja #779)
The helper read imports with a regex that treated a commented import as live, missed
`import (` written with a space, skipped a missing target inside packages/business/src
before checking it existed, and resolved targets lexically. It now blanks comments
(and ignores import forms inside strings and regular expressions) before matching,
counts a spaced or line-broken dynamic import, throws on a dynamic import whose
argument is not a plain string and on a file with an unterminated string, comment,
template or regular expression, checks every target exists and is a regular file
before the inside-own-directory skip, and refuses a target whose real path is outside
the real repository root. The output for the real tree is unchanged.

business-imports.test.mjs builds small trees under a private temp directory.
2026-11-02 20:14:25 -06:00
jason.woltje 1ff45c4ad6 Merge row 333: proxy usage and human model-key quotas, R4 (Gitea #1831) 2026-11-02 19:40:46 -06:00