Skip to content

T-7W0N-waypost-run-parameterised-task

State: closed/done · Impact: medium · Complexity: medium

Let a person supply a task’s arguments before it runs. Waypost finds just deploy <env>, shows it, offers to run it, and then runs just deploy with no environment — a command the catalog knows is incomplete. Both surfaces have the data to do better and neither uses it.

Task keeps argv and display as separate strings on purpose: display carries <required> / [optional] / *variadic placeholders and is not executable, argv is executable and omits the parameters entirely. The parameters themselves are modelled as data in Task.parameters. Nothing reads that field to ask for values.

LocationRole today
packages/ts/detect-runners/src/model.tsParam (name, required, variadic, default?) and Task.parameters. The field every surface has and none uses.
packages/ts/detect-runners/src/providers/just.tsPopulates parameters from just --dump, including defaults.
packages/ts/detect-runners/src/providers/rake.tsPopulates parameters from a rake task’s declared args.
packages/ts/waypost/src/tui.tsrunAndExit spawns argv, then prints takes <env> [tag] — the shape is computed after the incomplete command has already run.
packages/ts/waypost/src/web/api.tsrequestRun(id). An id and nothing else.
packages/ts/waypost/src/serve.tsPOST /api/run looks the id up in the catalog it built itself and hands the terminal over.

Every other provider emits parameters: [], so a change here is inert for them — the affected surface is exactly the recipes that declare arguments.

Enter on a task with parameters opens the full command in one editable field, cursor after a trailing space, with the parameter shape shown beneath it. Type, ⏎ to run, esc to back out. A task with no parameters is untouched: ⏎ runs it with nothing to dismiss.

One editable line rather than a field per parameter. A form would need to know how to quote, how to order variadics, and what to do when someone wants to pass a flag the recipe never declared — an editable command line has correct answers to all three for free.

TUI, where entry speed is the point, so this is a footer and not a dialog:

› deploy just ▸ ▸
─────────────────────────────────────────────────────────────────
just deploy <env> [tag] just
just build just
─────────────────────────────────────────────────────────────────
$ just deploy ▮
takes <env> [tag] ⏎ run esc back

Web, the same content as an inline panel rather than a modal, so dismissing it costs no click:

┌ Run ─────────────────────────────────────────────────────────────┐
│ $ just deploy ▮ │
│ takes <env> [tag] │
│ [Cancel] [Run ⏎] │
└──────────────────────────────────────────────────────────────────┘

The field is seeded from argv, never from display — display holds placeholders that a shell reads as redirections and globs, which is the reason the two fields exist separately at all.

POST /api/run takes an id and nothing else, deliberately: the port is reachable by any process on the machine and any site the browser visits, so accepting a command string would make it an arbitrary-code-execution endpoint. That stays true. The request grows one field — a list of argument strings — which the server appends to the argv it looked up itself. The binary, the leading arguments and the cwd still come only from the catalog, and nothing the client sends can replace them.

  1. Web first: gate requestRun behind the editable line when parameters.length > 0, and send the typed remainder as an argument list.
  2. Server: accept and append the argument list, keeping the binary and cwd catalog-only. A test asserts a crafted body cannot change the binary.
  3. TUI: the same line before runAndExit, reusing the shape string that function already builds for its after-the-fact notice.
  4. Document the behaviour on both surfaces’ guide pages.
LocationKindChange
packages/ts/waypost/src/web/view.tsmodifyRender the run panel.
packages/ts/waypost/src/web/input.tsmodifyEnter opens the panel for a parameterised task; esc closes it.
packages/ts/waypost/src/web/api.tsmodifyrequestRun carries the argument list.
packages/ts/waypost/src/serve.tsmodify/api/run appends the list to its own argv.
packages/ts/waypost/src/tui.tsmodifyThe footer entry line before runAndExit.
packages/ts/waypost/docs-site/src/content/docs/guide/web-view.mdxmodifyDescribe running a parameterised task.
  • AC-1: just deploy <env> runs with a supplied value from both surfaces, and the echoed command is the one that actually ran.
  • AC-2: Cancelling runs nothing and leaves the list where it was.
  • AC-3: A task with no parameters still runs on a single ⏎, with no extra step.
  • AC-4: A test proves a request naming a different binary or cwd cannot change what is spawned — the client contributes trailing arguments only.
  • AC-5: The seeded text comes from argv; a recipe whose display holds <placeholders> never puts them on the command line.
  • Remembering the last values used for a task. A history would need a per-task store and a way to clear it; nothing has asked for one yet.
  • Validating a value against its Param. The runner rejects what it does not accept, and duplicating that judgement here would be a second opinion that can be wrong.
  • none

Found while surveying what functional work waypost had left, 2026-08-31. The answer was that it had almost none, and this was the gap: the catalog can show you a task it cannot help you run. runAndExit printing takes <env> [tag] after spawning the incomplete command is the clearest symptom.


← Back to Tasks