From ed708fefe9f75589bfb626be4301b6c056cb2541 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravlev Date: Tue, 8 Sep 2026 12:10:10 +0200 Subject: [PATCH 1/4] docs(rfc): add Worker Lifecycle --- docs/rfcs/worker-lifecycle.md | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 docs/rfcs/worker-lifecycle.md diff --git a/docs/rfcs/worker-lifecycle.md b/docs/rfcs/worker-lifecycle.md new file mode 100644 index 000000000..d6b5e640e --- /dev/null +++ b/docs/rfcs/worker-lifecycle.md @@ -0,0 +1,82 @@ +--- +title: "Worker Lifecycle" +owner: "@johnthecat" +status: draft +--- + +# RFC — Worker Lifecycle + +## Summary + +A product has one worker. The host runs it while a reference to it is held and may stop it when none is. Modality work +takes a reference for as long as it is on screen or in flight. Acknowledging a product grants one short run so the +worker can set up before anything references it. + +## Motivation + +The [Product Manifest Format](product-manifest.md) defines the worker as the product's single background process and +does not say when it runs. A worker that runs for the life of the host is a signing-capable process per product running +unobserved. A worker that runs only with the product's app view cannot serve a chat room while the view is closed. Every +modality that calls a worker needs the same rule. + +### Requirements + +1. **Single.** A product has one worker process, however many modalities call it. +2. **Demand-driven.** The worker runs only while the host has work that only the worker can do. +3. **Stoppable.** The host may stop an unreferenced worker at any time and the product keeps working. +4. **Setup.** An acknowledged product gets a run before anything calls its worker. +5. **Additive.** No wire change; the host starts and stops the worker executable. + +## Approach + +The host keeps one reference count per product worker. The first reference starts the worker. When the count returns to +zero the host may stop it. Products do not ask for a worker to run. + +The design has three parts: + +- **References**: what holds the worker. +- **Acknowledgement grant**: the one run without a reference. +- **Product rules**: what a worker must tolerate. + +### References + +A reference is held for exactly as long as its work is on screen or in flight. Several references of one product hold +one worker. A modality names its holders in its own RFC. App and Widget executables hold no reference; their lifetime is +their screen. + +Reference holders, by modality: + +| Holder | Held while | +| ------- | ----------------------------------------------------------------------------------------------------- | +| Chat | A room the product serves is on screen, or a message addressed to the product is in flight. | +| Pocket | An artifact the product contributed is on screen. | +| Funding | The product is the selected provider of a funding flow, from selection until the flow settles. | +| Input | An input surface is open, or a `Custom` candidate is on screen ([Input Modality](input-modality.md)). | + +### Acknowledgement grant + +When the host acknowledges a product, it takes a time-limited reference on the worker. Acknowledgement may be a pin, an +addition to widgets or pocket, or the adoption of a new deployment of an acknowledged product. Nothing calls the worker +during the grant. The window is host policy, and the host releases the reference whether or not the worker is done, so +setup is idempotent and resumes on the next start. + +### Product rules + +- A worker tolerates being stopped whenever nothing references it, including mid-setup and while its output is on + screen. State that must survive goes through host storage. +- A worker does not read being started as user intent. +- A worker does no background work of its own. Proactive wakeups are the host's to schedule and are a separate RFC. + +Nothing crosses the wire. The worker learns it is needed by receiving a modality call. + +## Trade-offs + +- Setup that outlives the grant completes on a later start. +- A worker may start and stop several times in one pocket scroll. The host may keep an unreferenced worker warm; the + product may not rely on it. +- Considered and dropped: an always-on worker, a start per call without counting, one worker per modality. + +## Open questions + +- Whether a worker is told what started it, a grant or a reference. Nothing distinguishes the two from inside the + worker. From 66f94de73561a7c59a55daa0c04932298bad0e55 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravlev Date: Wed, 9 Sep 2026 15:07:25 +0200 Subject: [PATCH 2/4] docs(rfc): address review on Worker Lifecycle and record definition rule in rfc skill --- .claude/skills/rfc/SKILL.md | 4 ++- docs/rfcs/worker-lifecycle.md | 54 +++++++++++++++++------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.claude/skills/rfc/SKILL.md b/.claude/skills/rfc/SKILL.md index bea2f5163..03c74bf26 100644 --- a/.claude/skills/rfc/SKILL.md +++ b/.claude/skills/rfc/SKILL.md @@ -37,7 +37,7 @@ it states what the system does, and nothing else. ### Cut - Selling text and justification. State the rule; drop the reasoning and the "why this is good". -- Enumerations of examples. One case or none. +- Enumerations of examples. One case or none; one when the enumeration is the only illustration of a rule. - Em-dash appositions and dash-appended lists. Use plain sentences, a colon, or "because" and "so". - Bold-lead paragraphs that act as mini-sections. Fold them into the surrounding text or make a real heading. - Self-references: "this RFC", "this section", "below", "above", "stated under X". @@ -59,6 +59,8 @@ it states what the system does, and nothing else. `rust/crates/truapi/src/api/*.rs`. Every field and variant carries a doc comment. - A concept shared by several RFCs is defined in one and linked from the others. A dependent RFC states only what it adds. +- A term is defined once. Before cutting or moving a definition, find every use of the term and put the definition at + the best of them: the earliest use, or a Definitions section if the document has one. All other uses stay bare. - An inline list is a set of examples unless it is declared exhaustive: write "may be" or "such as". An exhaustive set goes in a table or an enum. diff --git a/docs/rfcs/worker-lifecycle.md b/docs/rfcs/worker-lifecycle.md index d6b5e640e..9d67abdc9 100644 --- a/docs/rfcs/worker-lifecycle.md +++ b/docs/rfcs/worker-lifecycle.md @@ -9,15 +9,14 @@ status: draft ## Summary A product has one worker. The host runs it while a reference to it is held and may stop it when none is. Modality work -takes a reference for as long as it is on screen or in flight. Acknowledging a product grants one short run so the -worker can set up before anything references it. +takes a reference for as long as it is on screen or in flight. Acknowledging a product, by pinning it or adding it to +widgets or pocket, grants one short run so the worker can set up before anything references it. ## Motivation The [Product Manifest Format](product-manifest.md) defines the worker as the product's single background process and -does not say when it runs. A worker that runs for the life of the host is a signing-capable process per product running -unobserved. A worker that runs only with the product's app view cannot serve a chat room while the view is closed. Every -modality that calls a worker needs the same rule. +does not say when it runs. A worker that runs only with the product's app view cannot serve a chat room while the view +is closed, and every modality that calls a worker needs one rule for when it runs. ### Requirements @@ -25,53 +24,52 @@ modality that calls a worker needs the same rule. 2. **Demand-driven.** The worker runs only while the host has work that only the worker can do. 3. **Stoppable.** The host may stop an unreferenced worker at any time and the product keeps working. 4. **Setup.** An acknowledged product gets a run before anything calls its worker. -5. **Additive.** No wire change; the host starts and stops the worker executable. +5. **Additive.** Nothing is added to the TrUAPI protocol. ## Approach The host keeps one reference count per product worker. The first reference starts the worker. When the count returns to -zero the host may stop it. Products do not ask for a worker to run. +zero the host may stop it. Products do not ask for a worker to run: the host starts and stops the worker executable, and +the worker learns it is needed by receiving a modality call. The design has three parts: - **References**: what holds the worker. - **Acknowledgement grant**: the one run without a reference. -- **Product rules**: what a worker must tolerate. +- **Stopping**: when the host stops a worker. ### References A reference is held for exactly as long as its work is on screen or in flight. Several references of one product hold -one worker. A modality names its holders in its own RFC. App and Widget executables hold no reference; their lifetime is -their screen. +one worker: a pocket artifact and an input surface over it are two references on the same worker. A modality names its +holders in its own RFC. App and Widget executables hold no reference; their lifetime is their screen. Reference holders, by modality: -| Holder | Held while | -| ------- | ----------------------------------------------------------------------------------------------------- | -| Chat | A room the product serves is on screen, or a message addressed to the product is in flight. | -| Pocket | An artifact the product contributed is on screen. | -| Funding | The product is the selected provider of a funding flow, from selection until the flow settles. | -| Input | An input surface is open, or a `Custom` candidate is on screen ([Input Modality](input-modality.md)). | +| Holder | Held while | +| ------- | -------------------------------------------------------------------------------------------------------- | +| Chat | A room the product serves is on screen, or a message addressed to the product is in flight. | +| Pocket | An artifact the product contributed is on screen. | +| Funding | A funding flow with the product as selected provider is in flight, from selection until it settles. | +| Input | An input round is in flight, or a `Custom` candidate is on screen ([Input Modality](input-modality.md)). | ### Acknowledgement grant -When the host acknowledges a product, it takes a time-limited reference on the worker. Acknowledgement may be a pin, an -addition to widgets or pocket, or the adoption of a new deployment of an acknowledged product. Nothing calls the worker -during the grant. The window is host policy, and the host releases the reference whether or not the worker is done, so -setup is idempotent and resumes on the next start. +When the host acknowledges a product, or adopts a new deployment of an acknowledged product, it takes a time-limited +reference on the worker. Nothing calls the worker during the grant; the product uses it for setup such as registering +its chat bot. The window is host policy, and the host releases the reference whether or not the worker is done, so setup +is idempotent and resumes on the next start. -### Product rules +### Stopping -- A worker tolerates being stopped whenever nothing references it, including mid-setup and while its output is on - screen. State that must survive goes through host storage. -- A worker does not read being started as user intent. -- A worker does no background work of its own. Proactive wakeups are the host's to schedule and are a separate RFC. - -Nothing crosses the wire. The worker learns it is needed by receiving a modality call. +- The host may stop a worker whenever nothing references it, including mid-setup and while its output is on screen, so + state that must survive goes through host storage. +- The host starts a worker when a reference forms, not when the user acts on the product, so a start is not user intent. +- The host runs a worker only while it is referenced, so a worker has no way to do background work of its own. Scheduled + wakeups are out of scope. ## Trade-offs -- Setup that outlives the grant completes on a later start. - A worker may start and stop several times in one pocket scroll. The host may keep an unreferenced worker warm; the product may not rely on it. - Considered and dropped: an always-on worker, a start per call without counting, one worker per modality. From 5ebb41aedabad749c9e37441f5aeaf291aca9420 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravlev Date: Wed, 9 Sep 2026 20:42:48 +0200 Subject: [PATCH 3/4] fix: small edit --- docs/rfcs/worker-lifecycle.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rfcs/worker-lifecycle.md b/docs/rfcs/worker-lifecycle.md index 9d67abdc9..6e62609bf 100644 --- a/docs/rfcs/worker-lifecycle.md +++ b/docs/rfcs/worker-lifecycle.md @@ -23,7 +23,7 @@ is closed, and every modality that calls a worker needs one rule for when it run 1. **Single.** A product has one worker process, however many modalities call it. 2. **Demand-driven.** The worker runs only while the host has work that only the worker can do. 3. **Stoppable.** The host may stop an unreferenced worker at any time and the product keeps working. -4. **Setup.** An acknowledged product gets a run before anything calls its worker. +4. **Setup.** A product gets a run before anything calls its worker. 5. **Additive.** Nothing is added to the TrUAPI protocol. ## Approach From 33a529a4b7e6ad3d5e5611e797cb6d55851bfa70 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravlev Date: Fri, 11 Sep 2026 13:42:26 +0200 Subject: [PATCH 4/4] docs(rfc): show three references on one worker --- docs/rfcs/worker-lifecycle.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/rfcs/worker-lifecycle.md b/docs/rfcs/worker-lifecycle.md index 6e62609bf..cbde3ed02 100644 --- a/docs/rfcs/worker-lifecycle.md +++ b/docs/rfcs/worker-lifecycle.md @@ -41,8 +41,9 @@ The design has three parts: ### References A reference is held for exactly as long as its work is on screen or in flight. Several references of one product hold -one worker: a pocket artifact and an input surface over it are two references on the same worker. A modality names its -holders in its own RFC. App and Widget executables hold no reference; their lifetime is their screen. +one worker: a chat room the worker serves, a pocket artifact it contributed, and a funding flow that selected the product +are three references on the same worker. A modality names its holders in its own RFC. App and Widget executables hold no +reference; their lifetime is their screen. Reference holders, by modality: