feat: provide per-project dev tools through mise #269
No reviewers
Labels
No labels
Agent
Ignore
Agent
In Progress
Agent
Planned
Agent
Planning
Agent
Ready
Agent
Triage
Compat
Breaking
Kind
Bug
Kind
Documentation
Kind
Enhancement
Kind
Feature
Kind
Security
Kind
Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
lucas/omp-deck!269
Loading…
Reference in a new issue
No description provided.
Delete branch "agent/issue-231-mise-dev-tools"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes: #231
Implements the plan on #231: mise in the image, deck-driven provisioning, a per-workspace config in Settings, and the session context that tells the agent how tools are provided. The "detect a missing tool and open a PR" loop is deliberately out of scope, and nothing baked into the image today was removed.
What is here
Image. A separate
misebuild stage pinned tov2026.11.7(MISE_VERSION+MISE_INSTALL_PATHare both honoured by themise.runinstaller — verified against the script, and the pinned install verified by running it). The runtime stage copies the binary and puts/data/.local/share/mise/shimsfirst onPATH. NoMISE_*_DIRoverride: withHOME=/dataevery mise directory already lands on the volume.MISE_NOT_FOUND_AUTO_INSTALL=0is set as an imageENV, so nothing written under/datacan re-enable shim auto-install;exec_auto_installandnot_found_system_fallbackkeep their defaults. Renovate learns the pin through a custom regex manager.Provisioning (
apps/server/src/mise/).provisionWorkspacematerializes the workspace's managed config, then installs in a scratch root at<dataDir>/mise/workspaces/<id>filled from the repository's default branch (origin/HEAD, else the default branch Forgejo reports), withMISE_CEILING_PATHSset to the scratch root's parent. Never from a session cwd —mise installauto-trusts what it loads and a trigger session sits in a clone of a PR branch. Status is in-memory per workspace, concurrent calls share one run, failures are recorded and logged and never thrown at a caller. Session create/resume kicks it off without awaiting.Per-workspace config. Migration
026,mise_config+mise_config_mode, throughWorkspace/WorkspaceDetails/UpdateWorkspaceRequest, thePATCH /workspaces/:idroute (which re-provisions in the background) and a textarea + mode select + Save in Settings → Workspaces, with the live phase underneath.overridewritesmise.local.toml;replaceadds a.miserc.tomlnarrowingoverride_config_filenames/override_tool_versions_filenames. Managed files carry a header, are appended once to.git/info/exclude, and a same-named file the deck did not write is never touched. Forgejo checkouts get the same config applied after clone and on restore.Session context. The guidance block is its own system-prompt entry (not
DEFAULT_PRELUDE, which a customprelude.mdwould hide).Deviation from the plan, and why
Section 4 asked to call
miseSessionContext(...)inside thesystemPromptclosure so the status refreshes per turn, flagged Unverified — confirm first. It does not hold: the SDK rebuilds the prompt only when its tool/model signature changes (session-tools.ts#computeAppliedToolSignature, which documents "callers must explicitly callrefreshBaseSystemPromptafter side-effecting changes"), so a background install finishing would never reach a live session. The plan's own contingency is implemented instead — the static block stays in the closure, andmiseStatusExtensionadds the status line frombefore_agent_start.One tightening in step 4 of provisioning: the on-disk fallback now runs only when no default-branch ref resolves at all. Falling back whenever the ref merely carries no mise config would read the user's working tree, which may sit on somebody else's branch.
Verification
Run against a throwaway deck DB, a real bare remote + clone, and the real pinned mise binary (not in the container; items 1, 2, 4 and 7 of the issue's list need an image build and a live trigger session and were not run):
jq = "1.7.1"provisions tophase: ok,sources: ["mise.toml"], and installs exactly1.7.1— while the workspace's checked-out branch declaringjq = "1.6"installs nothing. That is the pull-request-cannot-drive-an-install property, in the form that can be tested without a container.override:mise.local.tomlappears with the managed header,git status --porcelainstays empty,mise which jqin the workspace resolves to the pasted1.7.replace:.miserc.tomlappears andmise config lsin the workspace lists onlymise.local.toml— so the plan's main assumption (project.miserc.tomlhonoursoverride_config_filenames) holds and theignored_config_pathscontingency is not needed.phase: failedwith mise's error recorded, and logs one warning.apps/server/src/mise/materialize.test.tscovers the managed-file contract, including the refusal to clobber a user's ownmise.local.tomland the once-only git exclude block.bun test: 1693 pass, 0 fail.bun run typecheckand the web build are clean.