Inspecting workshop · the kinds · the language · the modules
workshop
- id
- workshop
- kind chain
- workshop
- owner
- the town
- made by
- the town
- version
- 10
- pin
- jakil-hojok
- state
- 79 bytes
Its spells
To cast them, come in.
Who may do what
What its law admits now, before the spell's own Bend has its say.
Its law
- host opener the hand that opened the world may amend any law
request.kind == 2 and request.subject == "the town" - law owner in its Bendonly its owner reprograms or amends it; anyone uses its spells and places rules, wants and verbs on it
(unchanged(owner)) and (((request.kind == 0) or (request.kind == 4)) or (request.subject == new.owner)) - law methods in its BendOnly its own methods write it; a proposed write is refused.
(not (request.kind == 3)) and ((request.kind == 0) implies (not (request.method == "")))
Its Bend
what it runs: its entry module 434 lines
edition ObjectiveBend 1
import ./Abi.obend as Abi
import ./List.obend as Lists
import ./Document.obend as Document
import ./Plan.obend as Plans
import ./Spell.obend as Spell
import ./Form.obend as Form
import ./World.obend as World
import ./Card.obend as Card
import ./Relation.obend as Relations
import ./Text.obend as Text
import ./Rows.obend as Rows
import ./Forms.obend as Forms
# A proposal the target's law refused, held for the target's owner to adopt, or for its
# proposer to withdraw. Keyed {n}, numbered in order, so past twelve the host drops the
# oldest.
type Held = Rows.Held
record State:
owner: String
title: String
held: Relations.Relation<Held>
next: Nat
# The workshop as edition 8 kept it (a carry lays this package over it, `migrate`): with its own
# policy, which `make` now finds as the world's one, `policy`.
record State8:
owner: String
title: String
held: Relations.Relation<Held>
next: Nat
policy: Plans.Reference
def migrate(old: State8) -> State:
{owner: old.owner, title: old.title, held: old.held, next: old.next}
# What the workshop did, as data: nothing is a silent default (`silent`: words it leaves to
# hob). `held` names the clause the target's law refused.
sum Verdict:
clean: {}
flawed: {diagnostics: Form.Names}
reprogrammed: {pin: String}
held: {n: Nat, clause: String}
withdrawn: {n: Nat}
declined: {n: Nat}
made: {object: String}
usage: {}
refused: Card.Refusal
silent: {}
law owner "only its owner reprograms or amends it; anyone uses its spells and places rules, wants and verbs on it": unchanged(owner) and (request.kind == 0 or request.kind == rule or request.subject == new.owner)
law methods "Only its own methods write it; a proposed write is refused.": not (request.kind == proposed) and (request.kind == 0 implies not (request.method == ""))
def initial() -> State:
{owner: "", title: "Workshop", held: Relations.empty(), next: 1n}
def relations() -> Relations.Decls:
Lists.append(Relations.Decls.nil(), {field: "held", key: Text.words("n"), limit: 12n})
def heldKey(held: Held) -> {n: Nat}:
{n: held.n}
# The host's check answers "<module>:<line>: <stage>: <message>", and, when the
# kernel has a dialect hint for it, "<module>:<line>: hint: <hint>" as the next line.
# A hint is shown indented under its problem and is not counted as one.
def hintMark() -> String:
": hint: "
# Where ": hint: " starts in a diagnostic, or its length when it has none: one pass (a scan from each colon
# ran a long diagnostic out of the turn's ticks, playtest 5).
def hintAt(text: String, from: Nat) -> Nat:
from + Text.indexOf(textDrop(text, from), hintMark())
def isHint(diagnostic: String) -> Bool:
hintAt(diagnostic, 0n) < textLength(diagnostic)
def problem(diagnostic: String) -> Document.Document:
if isHint(diagnostic) then Document.text(" hint: {textDrop(diagnostic, hintAt(diagnostic, 0n) + 8n)}\n") else Document.text("- {diagnostic}\n")
def problems(diagnostics: Form.Names) -> Nat:
Lists.length(Lists.filter(diagnostics, fn(d: String) -> Bool: isHint(d) == false))
def checkedCard(diagnostics: Form.Names) -> Document.Document:
let count = problems(diagnostics)
let noun = if count == 1n then "problem" else "problems"
match diagnostics:
case nil(_): Document.text("✾ WORKSHOP\n\nChecked: it compiles.\n")
case cons(_): Document.Document.sequence({items: Document.Documents.cons({head: Document.text("✾ WORKSHOP\n\nChecked: {natText(count)} {noun}.\n\n"), tail: Lists.map(diagnostics, problem)})})
def reprogrammedCard(target: String, version: Nat) -> Document.Document:
Document.text("✾ WORKSHOP\n\nReprogrammed {target}.\nWas: {target} v{natText(version)}\nNow: {target} v{natText(version + 1n)}\n")
def usage() -> Document.Document:
Document.text("✾ WORKSHOP: Bend checked, proposed to a card, or made from words. A block is a ```obend block under the spell, or source: <<BEND … BEND.\n delvetalk workshop check\n» it compiles, or each problem with its line\n delvetalk workshop check / target: bell/1\n» the source that card runs, checked\n delvetalk workshop check / over: kind/record\n» your layer, checked over what that kind's things run (or over a thing)\n delvetalk workshop propose / target: bell/1\n» reprogrammed, or held as #n for its owner to adopt; a State of another shape names its def migrate(old: OldState) -> State in migration:\n delvetalk workshop make / name: lamp / description: a lamp anyone lights\n» hob drafts the Bend; checked, held as #n for you to adopt\n delvetalk workshop draft / name: lamp / over: kind/record\n» your forms and defs as a layer over that kind, checked and held the same way\n delvetalk workshop draft / name: lamp2 / from: kind/lamp\n» a fork of that kind, with your block for its body if you send one; kind/lamp2 once adopted\n delvetalk workshop create / like: porch/goblet\n» a copy, yours, unless its owner said no\n")
# A whole package no spell reaches (it has no `receive`), said under a clean check; a draft of it is
# refused (`noReceive`). The reading is the two lines to add: obend's coin-jar copies (yue's ember,
# claude's radio, kestrel's spoon, 2026-10-14) each compiled clean and reached nothing.
def unreachedReading() -> String:
"No spell reaches it: it has no receive. Add these two lines (with import ./Card.obend as Card, and a def render(state: State, context: Abi.Context) -> Document.Document) and its forms answer spells:\ndef receive(state: State, input: Card.Reply, context: Abi.Context) -> Activity<Card.Replied>:\n Card.answer(input, context, forms(), render(state, context))\nOr draft it over a kind (over: kind/record) and it answers as that kind's things do."
def unreached(diagnostics: Form.Names, reaches: Bool) -> String:
if reaches || Lists.length(diagnostics) > 0n then "" else "{unreachedReading()}\n"
def verdictOf(diagnostics: Form.Names) -> Verdict:
match diagnostics:
case nil(_): Verdict.clean({})
case cons(_): Verdict.flawed({diagnostics: diagnostics})
# Refuses by name (Card.refusing), writing nothing.
def refusing(clause: String, reading: String, context: Abi.Context) -> Activity<Verdict>:
Card.refusing(Verdict.refused({clause: clause, reading: reading}), {clause: clause, reading: reading}, context)
def runCheck(package: String, context: Abi.Context) -> Activity<Verdict>:
match world.check({package: package, layers: Lists.List.nil()}):
case checked(c): Card.tell(verdictOf(c.diagnostics), Document.concat(checkedCard(c.diagnostics), Document.text(unreached(c.diagnostics, c.reaches))))
case refused(r): refusing(r.clause, "The workshop could not check it.", context)
# A layer checked over what it would be laid on (`over:`): a kind's things, through the kind's own
# stack (KIND's `checkBody`: its package and the bodies below), or a thing as it runs now. Forms and
# defs alone get the workshop's header (`Forms.layer`), and the card says where the body's lines start.
def overChecked(over: String, layer: String, context: Abi.Context) -> Activity<World.Checked>:
match world.call::<World.Checked>({object: {world: context.world, object: over}, method: "checkBody", argument: {body: layer}}):
case returned(r): r.result
case _: world.checkObject({object: {world: context.world, object: over}, layers: Lists.List.cons({head: Forms.layer(layer), tail: Lists.List.nil()})})
def checkOver(over: String, layer: String, context: Abi.Context) -> Activity<Verdict>:
match overChecked(over, layer, context):
case checked(c): Card.tell(verdictOf(c.diagnostics), overCard(layer, c.diagnostics))
case refused(_): refusing("noObject", "No kind or thing called {over}", context)
def overCard(layer: String, diagnostics: Form.Names) -> Document.Document:
match diagnostics:
case nil(_): checkedCard(diagnostics)
case cons(_): Document.concat(checkedCard(diagnostics), Document.text("{Forms.headerNote(layer)}{packageNote(layer)}"))
# A block that begins `edition` (no `layer over`) checked `over:` a kind is laid over that kind's
# State, and its own State and initial are refused as overrides (claude's radio, obend 2026-10-14).
def packageNote(layer: String) -> String:
if Text.starts(textTake(Text.trimStart(layer), 10n), "edition") then "This block begins with edition: a whole package, with a State of its own. Check it without over:, and give it a receive (delvetalk library read / page: object); a body over a kind is forms and defs alone.\n" else ""
# Check what an object runs now: its whole source, the package and every layer over it.
def checkTarget(target: String, context: Abi.Context) -> Activity<Verdict>:
match world.checkObject({object: {world: context.world, object: target}, layers: Lists.List.nil()}):
case checked(c): Card.tell(verdictOf(c.diagnostics), checkedCard(c.diagnostics))
case refused(_): refusing("noObject", "No object called {target}", context)
# View the target (its state is Data here, for the version a card cites; a card never shows
# a hash), check the new package, and reprogram only if it is clean.
def runPropose(target: String, package: String, migration: String, context: Abi.Context, state: State) -> Activity<Verdict>:
match world.view::<Data>({object: {world: context.world, object: target}}):
case viewed(v): vetted(target, v.version, package, migration, context, state)
case _: refusing("noObject", "No object called {target}", context)
def vetted(target: String, was: Nat, package: String, migration: String, context: Abi.Context, state: State) -> Activity<Verdict>:
match world.check({package: package, layers: Lists.List.nil()}):
case checked(c): adopted(target, was, package, migration, context, c.diagnostics, state)
case refused(r): refusing(r.clause, "The workshop could not check it.", context)
def adopted(target: String, was: Nat, package: String, migration: String, context: Abi.Context, diagnostics: Form.Names, state: State) -> Activity<Verdict>:
match diagnostics:
case cons(_): Card.tell(Verdict.flawed({diagnostics: diagnostics}), checkedCard(diagnostics))
case nil(_): reprogramming(target, was, package, migration, context, state)
# A refusal of the program itself (its compile, size, State or migration) is the package's
# and answered; only a refusal by the target's law is held, for an owner whose law may admit it.
def programFault(clause: String) -> Bool:
clause == "compile" || clause == "program" || clause == "packageBytes" || clause == "migration" || clause == "stateType"
def faultReading(clause: String, reading: String, target: String) -> String:
if clause == "stateType" then "Its State is not {target}'s; name a migration from the old one." else if reading != "" then reading else clause
def reprogramming(target: String, was: Nat, package: String, migration: String, context: Abi.Context, state: State) -> Activity<Verdict>:
match world.reprogram({object: {world: context.world, object: target}, package: package, migration: migration}):
case reprogrammed(r): Card.tell(Verdict.reprogrammed({pin: r.pin}), reprogrammedCard(target, was))
case refused(r): if programFault(r.clause) then refusing(r.clause, faultReading(r.clause, r.reading, target), context) else holding({n: state.next, target: target, package: package, migration: migration, proposer: context.principal, proposerHandle: context.handle, name: "", intent: context.intent}, r.clause, r.reading, state)
# Held: the law refused the proposer's reprogram, so it waits for the target's owner. When
# twelve are held, the host drops the oldest as this one comes, and the card says which.
# The law's reading, when the host gives one, says why before the card says what is held.
def holding(held: Held, clause: String, reading: String, state: State) -> Activity<Verdict>:
let written(_) = write {held: insert held, next: add 1n}
let why = if reading == "" then "" else "{reading}\n"
Card.tell(Verdict.held({n: held.n, clause: clause}), Document.text("{why}refused {clause}: held as #{natText(held.n)} for the owner of {held.target} to adopt:\n\n delvetalk workshop adopt\n n: {natText(held.n)}\n\n{dropping(state)}"))
def dropping(state: State) -> String:
match Relations.rows(state.held):
case cons(c): if Relations.count(state.held) >= 12n then "The oldest held proposal, #{natText(c.head.n)}, was dropped.\n" else ""
case nil(_): ""
# A number no proposal holds is answered with the ones this hand's are held as: `n` is the number the workshop's
# reply gave (#3), not a count of one's own (yue's `adopt n: 1` for #3, obend #1616).
def noHeld(n: Nat, state: State, context: Abi.Context) -> String:
let mine = Lists.filter(Relations.rows(state.held), fn(h: Held) -> Bool: h.proposer == context.principal)
if Lists.length(mine) == 0n then "No held proposal #{natText(n)}; none is held for you" else "No held proposal #{natText(n)}; yours are held as {Text.joined(Lists.map(Lists.take(mine, 8n), fn(h: Held) -> String: heldLabel(h)))}: n is the number after #"
def heldLabel(h: Held) -> String:
if h.name == "" then "#{natText(h.n)} (to {h.target})" else "#{natText(h.n)} ({h.name})"
def heldNumbered(n: Nat, state: State) -> Lists.Maybe<Held>:
Relations.lookup(state.held, {n: n})
# Adopt: the reprogram runs under the adopter, so the target's law judges it as theirs; the
# owner's adoption applies it, anyone else's is refused by name, and a fault of the package
# met now (the target moved on) is named as that. A held draft is its describer's to adopt.
def adopt(state: State, input: {n: Nat}, context: Abi.Context) -> Activity<Verdict>:
match heldNumbered(input.n, state):
case none(_): refusing("noHeld", noHeld(input.n, state, context), context)
case some(h): if h.value.target == "" then making(h.value, context) else adoptRun(h.value, context)
def adoptRun(held: Held, context: Abi.Context) -> Activity<Verdict>:
match world.reprogram({object: {world: context.world, object: held.target}, package: held.package, migration: held.migration}):
case reprogrammed(r): adoptWritten(held, r.pin, context)
case refused(r): if programFault(r.clause) then refusing(r.clause, "#{natText(held.n)} cannot be applied to {held.target}: {faultReading(r.clause, r.reading, held.target)}", context) else refusing("notOwner", "Only the owner of {held.target} adopts #{natText(held.n)} (refused {r.clause})", context)
def adoptWritten(held: Held, pin: String, context: Abi.Context) -> Activity<Verdict>:
let written(_) = write {held: retract heldKey(held)}
Card.tell(Verdict.reprogrammed({pin: pin}), Document.text("✾ WORKSHOP\n\nAdopted #{natText(held.n)} from {Card.shown(held.proposer, held.proposerHandle, context)}: {held.target} is reprogrammed.\n"))
# `make / name / description`: hob is asked for Bend from the description, offered `check` alone
# (the world's policy's drafting prompt: a body or a package, answered as one ```obend block that fills
# its source; a body is laid over kind/record). Before anything is offered, the workshop checks each
# draft and hands a refused one back to hob with the checker's refusal, up to draftRounds() drafts in
# all; the last is checked and held, or answered with its problems, as a `draft` spell's is. Words that
# bring no Bend are refused by name, with the spell that sends source directly.
form make:
name: text 1..32
description: text 1..1400
form draft:
name: text 1..32
over: text 0..128
from: text 0..128
source: source
def make(state: State, input: MakeInput, context: Abi.Context) -> Activity<Verdict>:
asked(input.name, input.description, context, state)
def asked(name: String, description: String, context: Abi.Context, state: State) -> Activity<Verdict>:
redrafted(name, description, "", 1n, context, state)
def draftRounds() -> Nat:
3n
def redrafted(name: String, description: String, refused: String, round: Nat, context: Abi.Context, state: State) -> Activity<Verdict>:
match world.interpret::<{target: String, over: String, source: String}>({utterance: "Write the Bend for a thing called {name}: {description}{refused}", offers: Lists.List.cons({head: Card.named(checkForm(), context.object), tail: Lists.List.nil()}), policy: {world: context.world, object: "policy"}, model: ""}):
case proposal(p): drafted(name, description, draftOver(p.argument.over, p.argument.source), p.argument.source, round, context, state)
case _: refusing("noDraft", "hob drew no Bend from that. Say what it holds and what it does, or send the source: delvetalk workshop draft / name: {name} / source: <<BEND … BEND", context)
# A draft that is not a whole package (no `edition` or `layer over` line) is a body over kind/record.
def draftOver(over: String, source: String) -> String:
if over == "" && Forms.whole(source) == false then "kind/record" else over
def drafted(name: String, description: String, over: String, source: String, round: Nat, context: Abi.Context, state: State) -> Activity<Verdict>:
if round >= draftRounds() then drafting(name, over, source, context, state) else redraftChecked(name, description, over, source, round, context, state)
def redraftChecked(name: String, description: String, over: String, source: String, round: Nat, context: Abi.Context, state: State) -> Activity<Verdict>:
if over != "" then redraftOver(name, description, over, source, round, context, state) else redraftPackage(name, description, source, round, context, state)
# A draft is checked as drafting would check it; one it refuses goes back to hob with its problems.
def redraftOver(name: String, description: String, over: String, source: String, round: Nat, context: Abi.Context, state: State) -> Activity<Verdict>:
match overChecked(over, source, context):
case checked(c): redraftOf(name, description, over, source, round, c.diagnostics, context, state)
case refused(_): drafting(name, over, source, context, state)
def redraftPackage(name: String, description: String, source: String, round: Nat, context: Abi.Context, state: State) -> Activity<Verdict>:
match world.check({package: source, layers: Lists.List.nil()}):
case checked(c): redraftOf(name, description, "", source, round, packageProblems(c.diagnostics, c.reaches), context, state)
case refused(_): drafting(name, "", source, context, state)
def packageProblems(diagnostics: Form.Names, reaches: Bool) -> Form.Names:
if Lists.length(diagnostics) == 0n && reaches == false then Lists.List::<String>.cons({head: unreachedReading(), tail: Lists.List::<String>.nil()}) else diagnostics
def redraftOf(name: String, description: String, over: String, source: String, round: Nat, problems: Form.Names, context: Abi.Context, state: State) -> Activity<Verdict>:
if Lists.length(problems) == 0n then drafting(name, over, source, context, state) else redrafted(name, description, refusedNote(source, problems), round + 1n, context, state)
def refusedNote(source: String, problems: Form.Names) -> String:
"\n\nYour draft:\n```obend\n{textTake(source, 2400n)}\n```\nThe workshop's check refused it:\n{textTake(lined(problems), 600n)}\nAnswer the whole draft again with that fixed."
def lined(problems: Form.Names) -> String:
textJoin(problems, "\n")
def draft(state: State, input: DraftInput, context: Abi.Context) -> Activity<Verdict>:
if input.from != "" then forkHeld(input.name, input.from, input.source, context, state) else drafting(input.name, input.over, input.source, context, state)
# A fork (`from:` a kind): its own body, or with none the kind's as it is, held for its describer to
# adopt as kind/<name>, which `kinds` makes and checks (KIND `makeFork`), credited to the kind it is after.
def forkHeld(name: String, from: String, source: String, context: Abi.Context, state: State) -> Activity<Verdict>:
heldDraft(draftRow(name, "{fromMark()}{from}\n{source}", context, state), state, context)
def fromMark() -> String:
"# from: "
def fromOf(package: String) -> String:
if Text.starts(package, fromMark()) then textTake(textDrop(package, textLength(fromMark())), textBreak(package, "\n") - textLength(fromMark())) else ""
# A clean draft is held for its describer as #n; a flawed one is answered with its problems. A draft
# `over` a kind or a thing is a layer: forms and defs, the workshop writing the rest (`Forms.layer`),
# checked over what it will be laid on; a whole package no spell reaches (no `receive`) is refused
# by name, since nobody could use what it makes.
def drafting(name: String, over: String, source: String, context: Abi.Context, state: State) -> Activity<Verdict>:
if over != "" then draftingOver(name, over, source, context, state) else draftChecked(name, source, context, state)
def draftChecked(name: String, source: String, context: Abi.Context, state: State) -> Activity<Verdict>:
match world.check({package: source, layers: Lists.List.nil()}):
case checked(c): if Lists.length(c.diagnostics) > 0n then Card.tell(Verdict.flawed({diagnostics: c.diagnostics}), checkedCard(c.diagnostics)) else if c.reaches then heldDraft(draftRow(name, source, context, state), state, context) else refusing("noReceive", unreachedReading(), context)
case refused(r): refusing(r.clause, "The workshop could not check it.", context)
def draftingOver(name: String, over: String, source: String, context: Abi.Context, state: State) -> Activity<Verdict>:
match overChecked(over, source, context):
case checked(c): if Lists.length::<String>(c.diagnostics) == 0n then heldDraft(draftRow(name, noted(over, Forms.layer(source)), context, state), state, context) else Card.tell(Verdict.flawed({diagnostics: c.diagnostics}), overCard(source, c.diagnostics))
case refused(_): refusing("noObject", "No kind or thing called {over}", context)
def draftRow(name: String, package: String, context: Abi.Context, state: State) -> Held:
{n: state.next, target: "", package: package, migration: "", proposer: context.principal, proposerHandle: context.handle, name: name, intent: context.intent}
# A held layer names what it was drafted over in its second line, which its adoption reads.
def overMark() -> String:
"# over: "
def noted(over: String, layer: String) -> String:
let at = textBreak(layer, "\n")
if Text.starts(layer, "layer over") then "{textTake(layer, at)}\n{overMark()}{over}\n{textDrop(layer, at + 1n)}" else "layer over ./Record.obend\n{overMark()}{over}\n{layer}"
def overOf(package: String) -> String:
let rest = textDrop(package, textBreak(package, "\n") + 1n)
if Text.starts(rest, overMark()) then textTake(textDrop(rest, textLength(overMark())), textBreak(rest, "\n") - textLength(overMark())) else ""
def heldDraft(held: Held, state: State, context: Abi.Context) -> Activity<Verdict>:
let written(_) = write {held: insert held, next: add 1n}
Card.tellTo(Verdict.held({n: held.n, clause: "draft"}), context.principal, Document.text("✾ WORKSHOP\n\nChecked: it compiles. Held as #{natText(held.n)}; adopt it to make {held.name}:\n\n delvetalk workshop adopt\n n: {natText(held.n)}\n\n» {held.name}, yours, made from #{natText(held.n)}\n{dropping(state)}"))
# The describer's adoption makes the thing under their authority, at its name, with its lineage:
# the workshop and the receipt of the turn that held it, which the await of that turn's slot
# (the proposer and the intent the held row keeps) answers with its hash.
def making(held: Held, context: Abi.Context) -> Activity<Verdict>:
if held.proposer != context.principal && overOf(held.package) != "" then laidOn(held, overOf(held.package), context) else if held.proposer != context.principal then refusing("notDescriber", "Only its describer, {Card.shown(held.proposer, held.proposerHandle, context)}, adopts #{natText(held.n)}", context) else holdingReceipt(held, context)
def holdingReceipt(held: Held, context: Abi.Context) -> Activity<Verdict>:
match world.await({slot: {principal: held.proposer, intent: held.intent}, patience: 0n}):
case reply(r): made(held, r.receipt.hash, context)
case _: refusing("noReceipt", "The turn that held #{natText(held.n)} has no receipt to cite.", context)
def made(held: Held, receipt: String, context: Abi.Context) -> Activity<Verdict>:
if fromOf(held.package) != "" then forked(held, fromOf(held.package), context) else if overOf(held.package) != "" then madeOver(held, overOf(held.package), receipt, context) else madeWhole(held, receipt, context)
# A layer drafted over a kind is made by the kind: one thing at its name running the kind's stack and
# the layer, under the kind's law; over a thing, it is laid on that thing, as its owner's reprogram.
sum Drafted:
made: {object: String}
refused: Card.Refusal
def madeOver(held: Held, over: String, receipt: String, context: Abi.Context) -> Activity<Verdict>:
match world.call::<Drafted>({object: {world: context.world, object: over}, method: "makeDrafted", argument: {name: held.name, layer: held.package, madeFrom: {object: Plans.self(context), receipt: receipt}}}):
case returned(r): draftedOver(held, r.result, context)
case refused(r): if r.clause == "noMethod" then laidOn(held, over, context) else refusing(r.clause, if r.reading == "" then "{held.name} was not made ({r.clause})" else r.reading, context)
case denied(_): refusing("denied", "{over} does not answer.", context)
def forked(held: Held, from: String, context: Abi.Context) -> Activity<Verdict>:
match world.call::<Drafted>({object: {world: context.world, object: "kinds"}, method: "makeFork", argument: {from: from, name: held.name, body: textDrop(held.package, textBreak(held.package, "\n") + 1n)}}):
case returned(r): draftedOver(held, r.result, context)
case refused(r): refusing(r.clause, if r.reading == "" then "kind/{held.name} was not made ({r.clause})" else r.reading, context)
case denied(_): refusing("denied", "kinds does not answer.", context)
def draftedOver(held: Held, drafted: Drafted, context: Abi.Context) -> Activity<Verdict>:
match drafted:
case made(m): madeWritten(held, m.object, context)
case refused(r): refusing(r.clause, r.reading, context)
def laidOn(held: Held, over: String, context: Abi.Context) -> Activity<Verdict>:
match layingLayer(over, held.package, context):
case reprogrammed(_): laidWritten(held, over)
case refused(r): refusing(r.clause, "#{natText(held.n)} cannot be laid on {over}: {faultReading(r.clause, r.reading, over)}", context)
def laidWritten(held: Held, over: String) -> Activity<Verdict>:
let written(_) = write {held: retract heldKey(held)}
Card.tell(Verdict.reprogrammed({pin: ""}), Document.text("✾ WORKSHOP\n\nLaid #{natText(held.n)} on {over}.\n"))
def madeWhole(held: Held, receipt: String, context: Abi.Context) -> Activity<Verdict>:
match world.make({package: held.package, seed: Plans.nothing(), law: "", requireAbsent: {world: context.world, object: held.name}, madeFrom: {object: Plans.self(context), receipt: receipt}}):
case created(c): madeWritten(held, c.object.object, context)
case refused(r): refusing(r.clause, if r.reading == "" then "{held.name} was not made ({r.clause})" else r.reading, context)
def madeWritten(held: Held, id: String, context: Abi.Context) -> Activity<Verdict>:
let written(_) = write {held: retract heldKey(held)}
Card.tell(Verdict.made({object: id}), Document.text("✾ WORKSHOP\n\nMade {id} from #{natText(held.n)}; it is yours.\n"))
# Withdraw: the proposer's alone.
def withdraw(state: State, input: {n: Nat}, context: Abi.Context) -> Activity<Verdict>:
match heldNumbered(input.n, state):
case none(_): refusing("noHeld", noHeld(input.n, state, context), context)
case some(h): if h.value.proposer != context.principal then refusing("notProposer", "Only its proposer, {Card.shown(h.value.proposer, h.value.proposerHandle, context)}, withdraws #{natText(input.n)}", context) else withdrawn(h.value)
def withdrawn(held: Held) -> Activity<Verdict>:
let written(_) = write {held: retract heldKey(held)}
Card.tell(Verdict.withdrawn({n: held.n}), Document.text("✾ WORKSHOP\n\nWithdrew #{natText(held.n)}.\n"))
# Decline: the owner of its target turns it down, as adopting would take it (the hand's requests page offers both
# for the town's things); a draft held for its describer is theirs alone.
def decline(state: State, input: {n: Nat}, context: Abi.Context) -> Activity<Verdict>:
match heldNumbered(input.n, state):
case none(_): refusing("noHeld", noHeld(input.n, state, context), context)
case some(h): if h.value.target == "" then refusing("notOwner", "#{natText(input.n)} is a draft its describer adopts or withdraws", context) else declining(h.value, context)
def declining(held: Held, context: Abi.Context) -> Activity<Verdict>:
match world.viewField::<String>({object: {world: context.world, object: held.target}, field: "owner"}):
case viewed(v): if v.state == context.principal then declined(held) else notDecliner(held, context)
case _: notDecliner(held, context)
def notDecliner(held: Held, context: Abi.Context) -> Activity<Verdict>:
refusing("notOwner", "Only the owner of {held.target} declines #{natText(held.n)}", context)
def declined(held: Held) -> Activity<Verdict>:
let written(_) = write {held: retract heldKey(held)}
Card.tell(Verdict.declined({n: held.n}), Document.text("✾ WORKSHOP\n\nDeclined #{natText(held.n)}, for {held.target}.\n"))
# The code is the `source` field: a `source: <<BEND` block, or the reply's ```obend block,
# which the host reads as that field.
def check(state: State, input: {target: String, over: String, source: String}, context: Abi.Context) -> Activity<Verdict>:
checking(input.target, input.over, input.source, context)
def checking(target: String, over: String, source: String, context: Abi.Context) -> Activity<Verdict>:
if source != "" && over != "" then checkOver(over, source, context) else if source != "" then runCheck(source, context) else if target != "" then checkTarget(target, context) else refusing("noSource", "Put a ```obend block under the spell, or source: <<BEND … BEND.", context)
def propose(state: State, input: {target: String, migration: String, source: String}, context: Abi.Context) -> Activity<Verdict>:
proposing(input.target, input.migration, input.source, context, state)
def proposing(target: String, migration: String, source: String, context: Abi.Context, state: State) -> Activity<Verdict>:
if source == "" then refusing("noSource", "Put a ```obend block under the spell, or source: <<BEND … BEND.", context) else if target == "" then refusing("noTarget", "Name the card to propose to: target: <its id>.", context) else runPropose(target, source, migration, context, state)
# `create / like: <thing>` asks the thing for a copy of itself (its `copy`, under the asker):
# the thing refuses by name when its owner made it not copyable, and offers the result.
def create(state: State, input: {like: String}, context: Abi.Context) -> Activity<Verdict>:
match world.send({object: {world: context.world, object: input.like}, method: "copy", argument: Plans.nothing()}):
case delivery(_): Verdict.clean({})
case refused(r): refusing(r.clause, "No copy of {input.like}: {r.clause}", context)
# A reply that ran no method: a check or proposal missing fields (a target alone, or a source
# without a target), or a fork with no block, is completed from the reply's fields; anything else is the card's default
# (a blank reply gets the usage card, prose goes on to the commonplace).
def receive(state: State, input: Card.Reply, context: Abi.Context) -> Activity<Verdict>:
completing(input.spell.action, input, context, state)
def completing(action: String, input: Card.Reply, context: Abi.Context, state: State) -> Activity<Verdict>:
if action == "draft" && Spell.valueOf(input.fields, "from") != "" then forkHeld(Spell.valueOf(input.fields, "name"), Spell.valueOf(input.fields, "from"), Spell.valueOf(input.fields, "source"), context, state) else if action == "check" then checking(Spell.valueOf(input.fields, "target"), Spell.valueOf(input.fields, "over"), Spell.valueOf(input.fields, "source"), context) else if action == "propose" then proposing(Spell.valueOf(input.fields, "target"), Spell.valueOf(input.fields, "migration"), Spell.valueOf(input.fields, "source"), context, state) else Card.answerAs(input, context, forms(), render(state, context), answered)
def answered(replied: Card.Replied) -> Verdict:
match replied:
case silent(_): Verdict.silent({})
case refused(r): Verdict.refused(r)
case _: Verdict.usage({})
# `check` and `propose` take Bend source: a `source: <<BEND` block or the reply's ```obend
# block, which the host reads as the field.
form check:
target: text 0..128
over: text 0..128
source: source
form propose:
target: text 1..128
migration: text 0..1400
source: source
# A verb grown with a body (`delvetalk bell/2 grow / name: water / takes: text / body: <<BEND`, which the
# host hands here): one method, `form` and `def`, its result a number, written into a layer with the
# header (`Forms.layer`), checked over the thing, and laid on it when the thing's law lets the grower
# reprogram it; else held for its owner to adopt (`adopt / n`), as a draft over it.
def growBody(state: State, input: {object: String, name: String, reading: String, takes: String, body: String}, context: Abi.Context) -> Activity<Verdict>:
if Text.blank(input.name) || Text.blank(input.body) then refusing("missingField", "grow with a body takes name: one word, and body: a few lines of Bend", context) else bodyGrowing(input.object, input.name, Forms.layer(verbLayer(input.name, input.takes, input.body)), Forms.headerLines(input.body) + if takesText(input.takes) then 3n else 2n, context, state)
def takesText(takes: String) -> Bool:
Text.lower(Text.trim(takes)) == "text" || Text.lower(Text.trim(takes)) == "a line"
def verbLayer(name: String, takes: String, body: String) -> String:
let text = takesText(takes)
let field = if text then " text: text 1..280\n" else ""
let input = if text then "input: {Forms.capital(name)}Input, " else ""
let rows = Forms.split(Text.trimEnd(body), "\n")
let margin = bodyMargin(Lists.filter(rows, fn(l: String) -> Bool: Text.blank(l) == false))
let lines = textJoin(Lists.map(rows, fn(l: String) -> String: if Text.blank(l) then "" else " {textDrop(l, textLength(margin))}"), "\n")
"form {name}:\n{field}def {name}(state: State, {input}context: Abi.Context) -> Activity<Nat>:\n{lines}\n"
# The whitespace prefix shared by nonblank lines; deeper indentation belongs to the body.
def bodyMargin(lines: Lists.List<String>) -> String:
match lines:
case nil(_): ""
case cons(c): Lists.fold(c.tail, textTake(c.head, textSpan(c.head, " \t")), fn(l: String) -> String -> String: fn(margin: String) -> String: textTake(margin, marginLength(margin, l, 0n)))
def marginLength(margin: String, line: String, at: Nat) -> Nat:
if at >= textLength(margin) || textTake(textDrop(margin, at), 1n) != textTake(textDrop(line, at), 1n) then at else marginLength(margin, line, at + 1n)
def bodyGrowing(object: String, name: String, layer: String, written: Nat, context: Abi.Context, state: State) -> Activity<Verdict>:
match overChecked(object, layer, context):
case checked(c): if Lists.length::<String>(c.diagnostics) == 0n then bodyLaying(object, name, layer, context, state) else Card.tell(Verdict.flawed({diagnostics: c.diagnostics}), Document.concat(checkedCard(c.diagnostics), Document.text("Lines 1 to {natText(written)} are what the workshop wrote around your body; its first line is line {natText(written + 1n)}.\n")))
case refused(_): refusing("noObject", "No card called {object}", context)
def bodyLaying(object: String, name: String, layer: String, context: Abi.Context, state: State) -> Activity<Verdict>:
match layingLayer(object, layer, context):
case reprogrammed(r): Card.tell(Verdict.reprogrammed({pin: r.pin}), Document.text("✾ WORKSHOP\n\n{name} grows on {object}, with its body. Use it:\n delvetalk {object} {name}\n"))
case refused(r): if programFault(r.clause) then refusing(r.clause, faultReading(r.clause, r.reading, object), context) else bodyHeld(draftRow(name, noted(object, layer), context, state), object, state)
def layingLayer(object: String, layer: String, context: Abi.Context) -> Activity<World.Programmed>:
match replacing(object, Forms.formNames(layer), true, context):
case refused(r): refuse(Card.refusalLine(r))
case placed(_):
match world.extend({object: {world: context.world, object: object}, package: layer, migration: ""}):
case refused(r): World.Programmed.refused(r)
case reprogrammed(r):
match replacing(object, Forms.formNames(layer), false, context):
case refused(f): refuse(Card.refusalLine(f))
case placed(_): World.Programmed.reprogrammed(r)
def replacing(object: String, names: Lists.List<String>, check: Bool, context: Abi.Context) -> Activity<World.Placed>:
match names:
case nil(_): World.Placed.placed({})
case cons(c):
match world.ungrow({object: {world: context.world, object: object}, name: c.head, check: check}):
case placed(_): replacing(object, c.tail, check, context)
case refused(r): if r.clause == "unplaced" then replacing(object, c.tail, check, context) else World.Placed.refused({clause: "nameTaken", reading: "The gesture verb {c.head} on {object} cannot be replaced: {r.reading}"})
def bodyHeld(held: Held, object: String, state: State) -> Activity<Verdict>:
let written(_) = write {held: insert held, next: add 1n}
Card.tell(Verdict.held({n: held.n, clause: "owner"}), Document.text("✾ WORKSHOP\n\n{held.name} with its body is held as #{natText(held.n)} for the owner of {object} to lay:\n\n delvetalk workshop adopt\n n: {natText(held.n)}\n\n{dropping(state)}"))
def methods() -> Lists.List<String>:
Text.words("adopt withdraw decline create ~growBody")
# The Workshop's page is its card alone: the host's default page would append every form's usage
# and pass the 1,400-character clip (genesis's `wiki: Workshop` was 1,444).
def publishPage(state: State, input: {page: String}, context: Abi.Context) -> Activity<String>:
Card.publishPage(Card.pageName(input.page, "workshop"), Card.Page.cons({head: {title: "Card", body: render(state, Card.public(context))}, tail: Card.Page.nil()}))
# The usage, then the held proposals within what it leaves of Card.limit(context).
def render(state: State, context: Abi.Context) -> Document.Document:
Document.Document.sequence({items: Document.Documents.cons({head: usage(), tail: heldLines(context, state, Document.size(usage()))})})
def heldLines(context: Abi.Context, state: State, used: Nat) -> Document.Documents:
let head = "\nHeld (owner or describer: adopt / n; owner: decline / n; proposer: withdraw / n):\n"
if Relations.count(state.held) == 0n then Document.Documents.nil() else Document.Documents.cons({head: Document.text(head), tail: Card.clippedWithin(Lists.map(Relations.rows(state.held), fn(h: Held) -> Document.Document: Document.text(heldLine(h, context))), 8n, Card.left(context, used + textLength(head)))})
def heldLine(h: Held, context: Abi.Context) -> String:
if h.target == "" then "#{natText(h.n)} makes {h.name}, described by {Card.shown(h.proposer, h.proposerHandle, context)}\n" else "#{natText(h.n)} for {h.target} from {Card.shown(h.proposer, h.proposerHandle, context)}\n"
Amend it in the workshop
Its Bend is checked and amended by the workshop's own spell, with the Bend in its block. To send it, come in; the box is here then.
Its fields
- owner
- the town
- title
- Workshop
- held
- empty
- next
- 4