Skip to main content

weft-sdp

Read when growing a program stage by stage: carving a run, supplying starting values, baking a trigger and firing it without activating, reusing completed work with a seed, freezing an accepted example, checking a new run against one, and what each refusal means.

来源信息

仓库
WeaveMindAI/weft
最近来源活动
2026年10月24日 17:50
检测到的 SKILL.md 语言
英语
星标
2,010
Forks
222

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
weft-sdp
description
Read when growing a program stage by stage: carving a run, supplying starting values, baking a trigger and firing it without activating, reusing completed work with a seed, freezing an accepted example, checking a new run against one, and what each refusal means.
# Sequential Diffusion Programming You build one stage against a real input, run it, and read the result before growing the next stage. You judge a result by what the program is meant to do: completion alone says nothing. If you catch yourself calling a run good because it completed, stop and write: "Wait. Read the result." Then inspect `weft events <run-id> --node <id>` and `weft logs <run-id>`. The terms below: - A [start] is where a run begins: a node named by `--from`, a group by `--group`, a trigger by `--fire`, or outputs supplied by `--emit`. - A [cut] is where a run stops: `--target` includes that endpoint, `--before` excludes it and everything beyond. - A [backup] is a value you supply on a [start]. The runtime waits for the run's real input first and uses the [backup] only after the real source closes without a value or no source participates. A real value wins, including null; a running source is never overwritten; errors and invalid real values stay errors, a [backup] does not hide them. - An [example] is a set of running parameters saved by `--save name` in `examples/name.json`, whose field names are camelCase (`keepFor`, `holdSecs`), like every JSON file weft writes. A [frozen example] is an [example] that `freeze` has given accepted output evidence, stored as `expected`. - [head] is the version this folder is on, with the run it came from when there is one. Each working copy has its own: this folder, another folder of the same project, the copy programs write into. A folder's first command starts it at the head of the project's most recently changed working copy. `weft branch` moves it, and so does every command that records a version. ## Carve the work you want to exercise `weft run` builds the current code and starts one run. You pass `--detach`, then read `weft runs --json`, `weft events <run-id> --node <id>` and `weft logs <run-id>`: failures and waiting states as well as outputs. You keep the user's graph intact while trying a stage: ```bash weft run --from classify='{"text":"the invoice is wrong"}' --target reply --save invoice --detach weft run --group triage='{"text":"the invoice is wrong"}' --detach weft run --from triage='{"text":"the invoice is wrong"}' --before publish --detach ``` - `--from node='{"port":value}'` starts that node and its downstream work; the downstream work brings its other input producers with it. A bare `--from node` supplies no [backup]. Repeat it for several [start]s. Walking upstream stops at every named from/emit and at triggers: those are the boundaries. - `--target node` and `--before node` set the [cut]; repeat either for several endpoints. A group or loop name includes the whole container with `--target`, or excludes it with `--before`. - `--group group='{"port":value}'` runs the whole group or loop alone; `--from group='{"port":value}'` runs it and continues downstream. You choose one form: `--group` cannot combine with from, emit, target or before. An included file uses its group alias, and a node inside one is named through that alias (`--from triage.up`, `--target triage.up`), which is also how `weft events` prints it. A group's entry and exit rows print under the group's own name (`triage`, with `boundary=in|out`), and `--node triage` shows them; there is no `triage__in` to name. - Whatever cut you choose, an input left with nothing is checked before the run starts: its wire's source is outside the run and you handed no value. If a node would skip without it (a required input, or the last member of a `@require_one_of` set that could get a value; the node's own, or one inside the group it enters, followed through every group, loop and included file), the run is refused, naming the input, its source, and what it feeds. Hand it a value at your start, or start further up so the source runs. A start is where the walk upstream stops, so neither `--group` nor `--from` runs what feeds the start's own ports: only what feeds the nodes AFTER it. On a `--seed` run a saved result can feed it instead. When the source is a trigger the run does not fire, the refusal says so: `reply.chatId gets nothing in this run: it only comes from the trigger ask, which this run does not fire, and reply cannot run without it. Fire it (--fire ask='<event>'), or hand the value at a start (...)`. `--emit` standing in for the trigger works too. - `--check`, with the same `--from`, `--group`, `--target`, `--before`, `--emit` and `--fire` (never with `--seed`, `--save` or `--detach`), only says whether that run could start (`the run can start`, or the refusal and exit `1`), building and running nothing. - `--feed start` runs what feeds a start: for each of its inputs you did not hand, the node that feeds it (through every group or include door on the way, however deep), and nothing above that node. A loop feeding it runs whole. The way to run one group whose connections come from outside: `--from 'hear.note={}' --feed hear.note --target hear.note`. Naming the feeders as starts yourself works too: when one start lies upstream of another, the run keeps both. - A start whose gate (`_should_flow`, its own or a surrounding group's) only comes from triggers the run does not fire, or from outside the run, is refused: it would skip and still report completed. `--feed` never opens a gate: it runs what feeds a start's inputs, and the gate is not one of them. Hand the gate a value at its own door, the start for its own gate (`--from 'start={"_should_flow": true}'`) and the group for a surrounding group's (`--from 'group={"_should_flow": true}'`, or `--group` if the run was a `--group` run), or `--fire` the trigger. - Without `--target` a run reaches everything downstream of its starts, so `--from db` runs every branch `db` feeds. `--target` keeps only what the target needs, from the starts down: it is a shape, not "run until you reach the target". - `--emit node='{"port":value}'` supplies that node's outputs without running its body. Repeat for several supplied nodes. An output with no consumer remains valid output evidence. For a generator port, you supply all its items in one array: `--emit batches='{"items":["first","second"]}'`. The runtime emits each item and then closes the port; `[]` closes it without an item. A list-typed ordinary port receives its array as one value, so you read the actual port type before choosing. If a branch needs a file its real source cannot give you here (a WhatsApp voice note needs a paired phone), you hand one from disk. Any value given to `--emit`, `--from`, `--group` or `--fire`, or saved in an example, may be a marker written exactly as in source, with the same meaning: `"@asset(\"samples/hello.ogg\", Audio)"` is uploaded into the project's storage and becomes the file value the port expects (its type names one kind: `Image`, `Video`, `Audio` or `Blob`), and `"@file(\"prompts/x.md\")"` becomes the file's text. A path is relative to the project root. Inside the JSON the inner quotes are escaped: ```bash weft run --emit receive='{"messageType":"audio","file":"@asset(\"samples/hello.ogg\", Audio)","seconds":3}' --target transcribe ``` A missing file is refused before the run starts, and so is a file on a port whose type does not take it, the same check as any other handed value. A saved example keeps the marker, so it reads the file afresh on every run. A [cut] inside an ordinary group stays at the requested node, and the group's `_should_flow` gate still applies: false stops the branch; true dispatches only the selected work. Loops are indivisible: you use the public loop name for a [start], `--group`, `--target`, or `--before`; a node inside the loop cannot be an endpoint. Trigger setup and infra preparation obey the same group cuts and loop restriction. ## Choose how a trigger participates You read the trigger's implementation and its ports before forming either payload. ```bash weft bake weft run --fire incoming='{"event":"the trigger wake payload"}' --detach weft run --emit incoming='{"message":"the emitted output value"}' --detach ``` If the program carries infrastructure, bring it up first with `weft infra start`. A run refuses while a node's container is not up, naming the node, and so does a bake whose trigger setup reads one; neither starts it. This is also how you test an infra node: live tests do not cover infra nodes for now, so you prove one inside a real program. Start its infra, then `--target` the node that reads it, with `--from` or `--emit` supplying the values upstream of it, so each run exercises the container and nothing else. A fire names exactly one trigger; its payload wakes that trigger, which runs and decides what to emit. An emit supplies the trigger's declared outputs directly. You choose one per trigger. Trigger ports receive their prepared settings through `weft bake`; a trigger cannot be a `--from` [start]. A run that reads nothing from a running [infra node] but its baked outputs uses the values the node saved instead of running it (baked outputs have nothing to do with `weft bake`). After you change what the node emits on one, name the node in the run (`--target <node>` or `--from <node>` always runs it) to test the new code. To update the value every other run gets, run `weft build` and then `weft infra rebake <node>`, which restarts nothing. A fire is durable or fast, and kept as long, as the trigger's `durable` and `keepRunsFor` say, and `--durable`, `--fast` and `--keep-for` override them for that run. A fire you start by hand is always recorded, even when the trigger has `recorded: false`. `bake` runs preparation and saves the trigger settings without listening. Changed code or configuration needs a matching new bake. Closed group gates can leave a trigger unprepared; you inspect the bake instead of forcing it. `weft activate` prepares and arms real listeners. `weft bake <project-id>` uses that project's registered build. If you deliberately use `--referenced`, you pass it to both bake and run. ## Reuse completed work while developing `weft run --seed --detach` takes eligible results from [head]'s run. If [head] is a version without a run, it finds a finished run on that version or its nearest ancestor. `--seed=<run-id>` takes them from any run of the project instead, one a trigger started or one that failed included, which is how a run that failed or broke off is picked up again after a fix (the `=` is required, since a bare word after `--seed` is an example name). `--root` starts a new tree with nothing to reuse, so it is refused beside a seed. If you also want that run's code back, `weft branch <run-id>` first: it writes that run's files into the folder, after recording the files you had as a version, and prints the `weft branch <id>` that comes back to them. - `--seed --seed-before classify` reuses earlier compatible work and runs `classify` and what follows. - `--seed --seed-until classify` also permits reusing `classify`. These flags limit reuse within the requested run; the [cut] limits what runs. A node whose saved output names a stored file that has been edited since, or is gone, cannot be reused by a node that runs again: the run is refused before it starts, naming the node, the port and the file, and you either hand the file in with `--emit` or run the node again with `--seed-before` it. Changed implementations, inputs, dependencies, failed work, and live handles can prevent reuse. A loop is reused whole. An entirely reused run is valid and does no new node work. This applies inside a saved or carved run too: `--from` chooses its boundary, not a forced rerun. Identical used [backup]s permit reuse; changing a used [backup] reruns its consumers. You omit `--seed` to run the selected work again. If you catch yourself reporting a seeded run without reading its warnings and inherited-node markers, stop and write: "Wait. What actually ran?" Then read them; never assume a requested endpoint was reusable. Builds include the whole catalog by default. The unchanged standard library uses a shared, finished worker image, so no project compilation is needed when that image is installed or published. Node edits, added nodes, or custom build settings need their own image, and building it compiles only the nodes no earlier build on this machine compiled. Adding an unchanged catalog node or editing graph configuration needs no new image, so you use ordinary `weft run` while iterating. `--referenced` opts into a separate, reduced build compiling only the graph's node types, which can rebuild when those types change; implementation edits still rebuild. ## Save parameters, then freeze accepted results `weft run name` runs the current program with the [example]'s parameters, frozen or not. You add `--seed` only when deliberately reusing results and omit it when reviewing how the current program answers the saved use case. ```bash weft run invoice --detach weft events <run-id> --full weft freeze invoice <run-id> --expect reply # After editing the program: weft run invoice --detach weft diff example:invoice <new-run-id> # After inspecting and accepting the new result: weft freeze invoice <new-run-id> --expect reply ``` `freeze` preserves that completed run's starting parameters and accepted output history together. A seeded whole run preserves its original starting inputs; a carved run preserves its [cut] and the inputs entering it. Replaying the [frozen example] recomputes the selected work on current code; interior reused results do not become hidden fixed inputs. `expected` holds the output evidence `diff` uses, including finite streams and closures. A closure a failure left reads `"closed": true` with `"failure": {"node": "query", "error": "..."}`, the node that broke and its message. You repeat `--expect node` to focus review on particular node outputs; focus changes comparison, not what runs, and a focused output that disappeared remains visible as a difference. Stored media compares by its content hash. `diff` presents changed values for your inspection. Differences are not a verdict and do not produce a failing exit status: you inspect the run's status separately, explain meaningful changes, and replace the [frozen example] only after accepting the new result. Running and diffing leave the accepted file untouched. `weft examples` lists [example]s and [frozen example]s; it does not certify them. ## Recover an example after changing the graph Removed input ports are ignored with a warning. Missing [start]s or [cut] endpoints are errors: you move the [cut] explicitly before running. ```bash weft run invoice --clear from --from newClassifier='{"text":"the invoice is wrong"}' --target reply --detach weft run invoice --clear group --from triage='{"text":"the invoice is wrong"}' --before publish --detach ``` Explicit `--from`, `--target`, `--before`, `--group`, and `--fire` replace the corresponding saved settings. Repeated new from flags build the replacement starting map. Emit flags replace the named supplied ports and keep the other saved emit entries. `--clear <field>` clears a saved field before those edits: `from`, `emit`, `target`, `before`, `group`, `feed`, `fire`, `instance`, `keeping` (what `--durable` or `--fast` saved), `keep_for` or `hold_secs`; repeat the flag for several fields. Duplicate newly supplied ports are errors. Values are never guessed for renamed nodes. You use `--save another-name` to keep revised parameters separately, or freeze an accepted new run to replace the [example]'s parameters and output evidence together. ## Read outside interactions A [frozen example] records how each wait of the run ended, with the question the node showed, and the incoming caller messages, for inspection; the runtime does not replay them on its own. When a run waits on a person, you compare the current question with the recorded one, end the wait the way the recorded entry says within the user's authority, and keep inspecting that same run. Each entry holds exactly one of these: - `payload`: the value the person answered with. Answer the current token with it. - `"skipped": true`: the person skipped the question. Skip it the same way, `POST /signal/{token}/skip`. - `gaveUp`: nobody answered. The run could not pause, held the wait in its worker until nothing had moved for its hold time, and gave the wait up; the text is the error the waiting node failed with. Leave the current one unanswered, so the new run gives up the same way when its own hold runs out. The entries cover every wait that ended, including timers and nodes that poll a job, not only questions to a person. You answer only the waits that ask a person: timers and polls run on their own, and answering one by hand would cut a real wait short. For live connections, you send the recorded messages through a new connection and review the new responses. `weft wake <run-id> <node>` resolves a pure time wait. A wait requiring a value must receive that value instead. Logs and inherited markers identify which earlier run supplied reused history; historical costs are not new charges. ## Keep a reviewable trail `weft checkpoint [label]` records a version without running anything, and the label is a name `weft branch` and `weft prune` accept in place of its id. `weft tree` shows every working copy with its head, then the versions and their runs; `weft branch <version|label|run-id>` puts this folder on a point in that tree. A switch loses nothing: the files the folder held become a version first, and the command prints the `weft branch <id>` that comes back to them. Another folder of the project has its own [head], but a program writing code with `ctx.code()` writes into the project's most recently changed folder unless it names a working copy, so its files can land in yours. `weft prune <version>` deletes history: the version, everything under it, and their runs, so you ask the user before you run it. It refuses when any working copy's [head], an activated version, the version the project's build runs, a frozen example's origin, a version that still supplies trigger settings, or a run that has not ended (a paused one counts) is inside that history. The first refusal names each thing in the way and what to do (`weft branch <version> --copy <copy>` or `weft forget-copy <copy>` for a working copy, `weft deactivate`, build another version, `weft freeze` from another run, `weft stop` the run). Once those are clear, a second one can still come: `these versions still supply trigger settings or running runs; stop the runs and replace or wipe those trigger settings before pruning`, which names no version. No flag skips either (`--yes` only skips the question). The trigger bakes made from the pruned versions are deleted with them. After an edit, you run the relevant saved use cases, inspect their diffs, and report what actually ran, what changed, why a result is acceptable or still wrong, and the run ids.
在 GitHub 查看