Subjects

The running jobjob_7Hq2 · working · $49.00 held

A subject is what outlives the job, keyed by an input or a receipt. It keeps typed facts across jobs, insert-only, tells every job that read a fact when an amendment restates it, holds one job of a Service per period, names who is of record, and keeps its open clocks in someone's hands between jobs and after a client leaves.

subject answers one question: what does this work belong to, that the next job should remember?

One line names the subject, the period each job holds, and the fact it keeps:

services/glossary.ts
  subject: { kind: 'account', key: 'account', period: 'year', keeps: { glossary: 'glossary' } },

The second year reads what the first one kept, with no input to carry it:

captured/service-subject.txt · 202 · job_7Hq3 · delivered · $19.00 charged · magic values: none
→ POST /glossary {"account":"acme","year":"2026","terms":["Premises"],"max_usd":19,"key":"glossary-2026"}
  output  {"glossary":["Lessee","Lessor","Premises"],"last_year":["Lessee","Lessor"]}

On the Service

KeyWrittenLeft outDoes
subjectSubjectSpecnoneWhat outlives the job: a subject keyed by an input or a receipt, the member of record on it, its kept facts, its periods, and the Service that watches its clocks.

subject, written out

KeyWrittenLeft outDoes
kindstringrequiredWhat it is, in your words: 'patent family', 'trademark', 'company'.
key(keyof I | keyof O) & stringrequiredThe input, or the output a receipt fills, whose value names it: application_number, ein.
of_record{ role: RoleWord; named_at: <step>; custodian?: (RoleWord | 'brand')[] }noneThe member of record: whoever attests at named_at stays of record on the subject across jobs. Withdrawal names a successor; a member whose credential or coverage lapses leaves it vacant, and custodian covers it, rung by rung, then the brand.
keepsKnoneFacts the subject keeps across jobs, each from the output that writes it: { carryforwards: 'carryforwards', tax: 'tax' }, typed as that output is. Written only when a job ends accepted, never when it is canceled or lapses, and keyed by the period the job held. Insert-only: each value records { value, job, step, at, period }, and nothing is overwritten.
period(keyof I & string) | ((job) => string) | { of; ends?; many? }noneOne live or ended job of this Service per subject per period: the input that names the period, or a function of the order. A second order for a period answers 409 period_taken, its next the follow-on the end offers, such as an amendment. A canceled job frees its period. ends gives the period's last day as MM-DD, for a fiscal year; many: true holds any number of jobs per period, as amendments do.
stewardAnyServicenoneThe Service that keeps the subject's clocks once a job ends with one still running: ordered with the subject's key, it keeps the date in someone's hands. Until it is, the customer portal offers it beside an ask in which the client acknowledges they own the date, and nothing lapses into a gap between jobs.

In your code, on the job

KeyWrittenLeft outDoes
subjectSubjectViewrequiredThe job's subject, as it stands: its kept facts, and the same Service's work on it last period.

keeps maps a fact to the output that writes it. A fact is written only when a job ends accepted, keyed by the period that job held, and never overwritten.

Your code reads it on the job:

In your codeReads
job.subject.fact('glossary')On a job with a period, the latest value from the closest period before its own; on one without, the latest written. Undefined until an accepted job writes it.
job.subject.fact('glossary', { period: '2025' })The latest value written for that period. 'prior' and 'latest' name the two defaults.
job.subject.fact('pages', { period: '2027', all: true })Every value of that period and the periods inside it, 2027-Q1 to 2027-Q4, oldest first.
job.subject.fact('glossary', { all: true })Every value, oldest first.
job.subject.prior('draft')That step's committed output on the latest ended job of this Service with a lower period.
ViewOf<typeof glossary>The job, typed from the definition, so fact('glossary') is a string[]. Code shared across Services takes AnyView and names the type: fact<string[]>('glossary').

So a late job for an earlier period never changes what the next one reads.

The record keeps every fact a job read, with the value's id. A newer value written to a period another job read raises fact.restated on that reader and the subject:

ThenWho
the reader's end offers its follow-on, in the customer portal and the owner consoleby default
the plan mints the follow-ona plan with on.event: { 'fact.restated': amendedReturn }

apis.tax amends 2025 in November 2027, and the 2026 return read its carryforwards. The Tax Plan routes the event, and amendments sit outside its allowances, so the mint is offered at its price:

captured/tax-cases.txt · magic values: owner.email = signs@example.test, metadata.sandbox.outside = {"reject":{"file.US-NY":["ein_mismatch"]}}
$ npx services.dev events --since 2027-11-30 --on sbj_7Hq2 --test-clock tc_7Hq4
evt_9Ts1  job.accepted         job_4Ty3  amended-return · period 2025 · the subject keeps carryforwards 2025 and tax 2025, newer values
evt_9Ts2  fact.restated        sbj_7Hq2  carryforwards 2025 · read by job_4Tx3 (period 2026, at compute 2027-07-13)
captured/tax-cases.txt · magic values: owner.email = signs@example.test, metadata.sandbox.outside = {"reject":{"file.US-NY":["ein_mismatch"]}}
$ npx services.dev jobs get job_4Tx3 --test-clock tc_7Hq4
job_4Tx3 · accepted · annual-return · period 2026 · $0.00 on sbn_7Hq2
  read     carryforwards 2025 · fct_7Hq4 (job_4Tx2) · restated by fct_7Hr9 (job_4Ty3) 2027-11-30

GET /subjects/:id is the whole record: every job that touched it, its history, each fact with every value, and the periods its jobs hold:

captured/service-subject.txt · 200 · magic values: none
→ GET /subjects/sbj_7Hq2
{
  "id": "sbj_7Hq2",
  "object": "subject",
  "livemode": false,
  "kind": "account",
  "key": {
    "field": "account",
    "value": "acme"

A clock with lives_on: 'subject' keeps running after the job that opened it ends. An agent reads a subject through its clocks, on any surface:

captured/subject-tabs.txt · magic values: none
$ npx services.dev clocks list subject=sbj_7Hq2 --json   # the subject's clocks, its job ended
      "id": "clk_7Hq2",
      "name": "franchise_tax",
      "lives_on": "subject",
      "status": "open",
captured/subject-tabs.txt · 200 · magic values: none
$ curl http://localhost:8787/clocks?subject=sbj_7Hq2   # the subject's clocks, its job ended
      "id": "clk_7Hq2",
      "name": "franchise_tax",
      "lives_on": "subject",
      "status": "open",
captured/subject-tabs.txt · magic values: none
→ tools/call list_clocks {"subject":"sbj_7Hq2"}   # the subject's clocks, its job ended
      "id": "clk_7Hq2",
      "name": "franchise_tax",
      "lives_on": "subject",
      "status": "open",
captured/subject-tabs.txt · magic values: none
await lingua.clocks.list({"subject":"sbj_7Hq2"})   // the subject's clocks, its job ended
      "id": "clk_7Hq2",
      "name": "franchise_tax",
      "lives_on": "subject",
      "status": "open",

Every subject with an open clock has someone who answers for it. When a job ends with a clock still running, the record says so, and the client is asked to own the date:

captured/subject-watch.txt · magic values: none
→ GET /owner/jobs/job_7Hq2
  clock.outlived · franchise_tax · 2027-03-25 · the client owns it, or orders Company Watch
  ask.opened · own_dates · sign · buyer

steward names the Service that keeps the subject's dates instead. The customer portal offers it beside the ask: mark the date done, hand it to the steward, or sign that it is theirs:

captured/subject-watch.txt · magic values: none
→ GET /portal
  franchise tax · due 25 Mar 2027 · you answer for it · notice 23 Feb
    [Mark it done]  clock.satisfy · POST /clocks/clk_7Hq2/satisfy
    [Keep it with Company Watch · $9]  service.order · POST /company-watch
    [I'll own this date]  ask.complete · POST /asks/ask_7Hq2
captured/subject-watch.txt · 200 · OK · 1 clock · magic values: none
→ GET /clocks?job=job_7Hq2
  clk_7Hq2 · franchise_tax · due 2027-03-25 · buyer · open · on the subject · every year · the client signed for it 2026-10-25

Once a job of the steward joins the subject, the ask is voided. Nothing lapses into a gap between jobs:

captured/subject-watch.txt · magic values: none
→ GET /owner/jobs/job_7Hq3
  ask.opened · ask_7Hq3
  ask.voided · ask_7Hq3 · Company Watch keeps the dates now: job_7Hq4.

A client who leaves gets the same hand-back: when a canceled plan's last cycle ends, each open date the brand or a member of record answered for is offered to the client. apis.tax's steward, Carryforward Watch, is $9 a year, so the 2046 carryforward keeps an owner:

captured/tax-cases.txt · 200 · the disengagement letter · Acme Robotics · 2029-01-20 · magic values: owner.email = signs@example.test, metadata.sandbox.outside = {"reject":{"file.US-NY":["ein_mismatch"]}}
→ GET /portal/subjects/sbj_7Hq2/letter
  Your Tax Plan ended on 20 January 2029. These dates are still running, and from today they are yours to keep:
    2029-04-17  refund claims on your 2025 return close, and so does the IRS's time to assess it
    2030-07-11  the same, for your 2026 return

of_record names the member who attests at a step. They stay of record on the subject across jobs, and a clock with owner: 'of_record' is theirs. Withdrawing names a successor who holds the role's credential. When the member's credential or coverage lapses, the seat turns vacant and custodian covers it, rung by rung, then the brand. Of record walks it.

With period, a subject holds one job of the Service per period, and a second order for one is refused with its follow-on: One job per period.

A subject keeps no timer of its own. Its jobs, its clocks and the people on it move it:

WhenThe runtimeEvent
a job ends acceptedwrites each kept fact, keyed by the period that job heldjob.accepted
a newer value is written to a period another job readnames each reader; the reader's end offers its follow-on, or a plan's on.event mints itfact.restated
a job ends with a clock still runningkeeps the clock on the subject, and asks the client to own the date until a steward's job joinsclock.outlived
the member of record's credential or coverage lapsesthe seat turns vacant, and the custodian covers it, rung by rung, then the brandclock.vacant
a canceled plan's last cycle endsoffers the client every open clock the brand or a member of record answered forclock.outlived
the member of record withdrawsnames a successor who holds the role's credential, who accepts itsuccession.started, succession.accepted
AppShowsActs
Customer portaleach subject in your word for it: one row per period, each kept fact with the job that wrote it, who is of record, and its dateskeep a running date with the steward Service, or sign that it is yours
Delivery portalthe subjects the member is of record on, with the dates they answer forwithdraw, naming a successor
Owner consolea seat of record left vacant, and any date on a subject no one answers forassign a member who holds the credential

A subject has no price of its own: each job on it, and its steward, charges on its own terms. It keeps the plan it was ordered under, so its clocks run to their end after a downgrade. Subjects, periods and kept facts run live on Launch; the custodian ladder and a steward on Team. Every kind of time, by plan: Keeping time.

OpAPICLIMCPSDKSiteDocsApp
subject.listGET /subjects, GET /owner/subjectssubjects listnever: an agent reads its clocks with list_clocksnever: the SDK reads its clocks with clocks.list()1 error pagecustomer portal, owner console
subject.getGET /subjects/:id, GET /owner/subjects/:idsubjects get <subject>never: an agent reads its clocks with list_clocksnever: the SDK reads its clocks with clocks.list()1 error pagecustomer portal, owner console
subject.withdrawPOST /subjects/:id/withdrawsubjects withdraw <subject> <successor>never: a person actsnever: a person acts2 error pagesdelivery portal
subject.calendarGET /subjects/:id/calendar, .icssubjects calendar <subject>never: an agent reads the dates with list_clocksnever: the SDK reads the dates with clocks.list()customer portal
member.subjectsGET /me/subjectsme subjectsnever: a person actsnever: a person actsdelivery portal

Objects: sbj_. Events: fact.restated, succession.started, succession.accepted.

Operations has every column of every row.

CodeCategoryHTTPWhy
not_foundinvalid_request404Nothing has that id.
check gateWhy
clock_unownedA clock's owner can never resolve: a role the Service doesn't have, or of_record with no member of record on its subject. It would run vacant, with no one to answer for the date.
When the file won't loadThrown byWhy
writes_unknownService()Service Glossary: subject.keeps.glossary is written from terms, which isn't an output. A kept fact is written from the output: glossary.

The lines that use it in the example brands, as their files write them, and what check read aloud for each file.

legal · services/docket-watch.ts · 10 lines
  subject: { kind: 'patent family', key: 'matter' },
npx services.dev check · captured/legal-walk.txt
services/docket-watch.ts · 10 lines · $12 per month, charged at the start of each month.

The legal example runs every one of its files, captured.

tax · services/annual-return.ts · 45 lines
  subject: {
    kind: 'taxpayer', key: 'ein', period: { of: 'tax_year', ends: job => job.input.year_end ?? '12-31' },
    of_record: { role: 'signer', named_at: 'sign', custodian: ['signer', 'brand'], until: 'assessment' },
    keeps: { carryforwards: 'carryforwards', tax: 'tax', paid: 'payment_usd', first_installment: 'first_installment' }, steward: carryforwardWatch,
  },
npx services.dev check · captured/tax-check.txt
services/annual-return.ts · 45 lines · $1,800 per job, filed 5 days before its deadline, 20% off if late, or we pay the penalty and its interest, up to $5,000, after we ask the IRS to take it off, charged when file is accepted.

Two clients change, amend and leave in apis.tax.

A year, and the next reads one subject's facts across two years.