Osy#betaRun it on Osyrin: osyrin.com

Reference / Local

Finding a kit, and learning one from its tests

osy kits find "<need>" [--offline] [--json] · osy kits [name] [--full] [--tests <file>] [--project <dir>] [--json] · osy kits new <Name> [--http <host>] [--owner <Owner>]

Lists the kits (packages) this project can build on — each one's contracts, the kits that implement them, and what this app wired — and opens one kit's front door: its purpose, its test app, and every test it keeps.

stablelocalclipackageskits

Summary#

osy kits answers two questions offline. Which kits can this app use, and how do they fit together? — every package in scope, the contracts each declares, which kits implement each, and what THIS app put in each contract-typed setting. How do I use this one? — osy kits <name> prints the kit's purpose, then its test app (the smallest real program that uses it), then the name of every test it keeps.

Signature#

osy kits [name] [--full] [--tests <file>] [--project <dir>] [--json]

name is a package name (Osysharp.Mail), its last segment (mail) or its directory in this repository (mail-resend). --project points at the app whose wiring to report (default: the current directory).

Description#

What is in scope, and who implements what?#

With no name, osy kits lists every kit this project can see — the ones in the repository's workspace, the ones this app's lock pins, and the ones the platform carries — each with its version, where it comes from and its summary. A kit that declares a contract (a public interface other kits implement) lists it with every implementation in scope; a kit that implements one says which:

Osysharp.Mail 0.2.0 · workspace:kits/mail  used by this app
  mail an app sends — one message shape, a pluggable sender, an outbox that delivers after the commit …
  contract IMailSender — implemented by ResendSender (Osysharp.Mail.Resend)
Osysharp.Mail.Resend 0.1.0 · workspace:kits/mail-resend
  implements IMailSender (Osysharp.Mail) with ResendSender

What did this app wire?#

Run in an app, the listing opens with what the app put in each contract-typed field of an app setting — the app.X = new Setup { Field = new Implementation { … } } lines — or, when it put nothing, the declaring kit's own words for what that means:

What MailResendKitTests wired
  app.Mail.Sender (IMailSender) = ResendSender (Osysharp.Mail.Resend)

…and in an app whose app.Mail names no sender:

  app.Mail.Sender (IMailSender) = none — every message is HELD in the outbox — the app works, and nothing is sent.

The same answer is in osy model --json, as contracts and contractWiring, for a tool.

How do I learn one kit?#

osy kits <name> prints, in this order:

  1. Its purpose — the package's summary, then the comment block at the top of its package.osy, where its author explains what it is for and what it deliberately is not.
  2. Its contracts — each interface, its members, which of them have a default, and who implements it.
  3. Its test app, whole — tests/app.osy and its model files. It is the smallest correct program that uses the kit: the use lines, the setting the app fills, the entity it extends.
  4. Every test, by name, grouped by file — the promises the kit keeps, one line each; a parked one is marked.

--full prints the test files whole, and --tests <file> prints one. A kit fetched from a release shows them too: they ship beside the package as the release's examples.zip, and are cached with it. A release published before kits shipped their tests says so, rather than showing nothing. To connect the kit, osy kits <name> --surface shows each seam with the test app's code that connects it (Connecting a kit — each seam with the code that connects it).

osy kits mail                       # purpose, contracts, the test app, the test names
osy kits mail --tests mail.test.osy # one test file, whole
osy kits mail --full --json         # everything, for a tool

A kit by what it does — osy kits find#

osy kits find "<need>" ranks every kit in scope by what it says it does — its summary and the comment at the top of its package.osy, its need: lines, the names and first doc sentences of what it declares, its events' labels — so you can ask in your own words. A word most kits share counts for little; a bridge ranks below the kits it completes (it arrives by itself). Each match shows what the next step needs: the events it raises, its actions and slots, the kits it uses, the bridges that complete it with kits this project already uses, and the domain of its test app — a kit proven by a cycle workshop is not secretly a helpdesk. Unless --offline, the published packages on GitHub are searched too — and the answer is kept for 30 minutes per question (~/.osy/package-search/), because GitHub allows 10 unauthenticated searches a minute. When that limit is spent, the last answer to the same question is shown with its age, or, with none, one line says the kits in scope were ranked alone; OSY_GITHUB_TOKEN (or gh auth login) raises the limit to 30.

$ osy kits find "ask customers how it went after the work is done"
Osysharp.SatisfactionSurveys 0.5.0 · workspace:kits/satisfaction-surveys used by this app
  asking the person a piece of work served how it went — one good-or-bad answer and an optional comment per subject and …
  matched: ask, customer, went, after, work, done
  events   FeedbackAnswered
  uses     Osysharp.Identity, Osysharp.Workflow, Osysharp.Mail, Osysharp.EmailTemplates, Osysharp.Ui
  completes with what you have: Osysharp.Privacy.SatisfactionSurveys (completes Osysharp.Privacy + Osysharp.SatisfactionSurveys)
  proven by: a cycle workshop's repair bookings

A kit matches when ONE thing it says — its name, its summary, one need: line, one event — holds most of the need's words. When none does, the nearest are shown instead, under a line saying none says all of it: a kit NAMED for one of the need's words (Osysharp.Csv for "import a CSV file of rows"), then its family (Osysharp.Payments.Stripe for "card payments"), then a kit that says two of the need's rarer words together. Each shows the words it matched:, so read its summary before deciding it does what you need. "email" is read as "mail", and -ize as -ise.

$ osy kits find "send notifications by email or sms"
No kit in scope says all of that — the nearest, each saying part of it (`matched:`). …
Osysharp.Sms 0.5.0 · workspace:kits/sms
  matched: send, sms
Osysharp.Notifications 1.2.0 · workspace:kits/notifications
  matched: notification, mail
Osysharp.Mail 0.11.0 · workspace:kits/mail
  matched: send, mail

When nothing is even near but ONE of the need's words finds a kit on its own, it names those words and the kits each finds — so a three-word need that missed does not read as "no kit does this" (fewerWords in --json):

$ osy kits find "waiver consent signature"
No kit says all of that together — but fewer words find these. Read their summaries before writing your own:
  osy kits find waiver   → Osysharp.Qualifications

When no word finds anything either, it says so: write the subsystem as a general kit beside your app (below), proven by a test app of a different domain.

When no kit covers it — a local kit#

What an app needs that no kit covers — its own kind of record (a pass, a credential, an agreement), an external API (a health service, a carrier, a bank) — is best built as a kit BESIDE the app rather than woven into it. osy kits new makes one, in one of two shapes:

osy kits new Passes                              # → kits/passes/, a DATA kit
osy kits new GoogleHealth --http api.health.test # → kits/googlehealth/, an INTEGRATION kit for that host
osy kits new Passes --owner Acme                 # the owner, said outright

The package is named <Owner>.<Name>. The owner is read from the repository's origin (github.com/acme/fitness → Acme), so the name already points where the kit would be published; with no origin on github.com it is the app's own name (app Climb → Climb.Passes), and --owner names another. Any owner is fine while the kit stays in the app: osy publish is where the owner is fixed, and it asks for one if the kit is still named after its app (package.osy).

A data kit (the default) is an entity, the rules over it, and a test app in which it runs alone:

filewhat it is
package.osythe manifest — no egress, no secrets; it builds on Osysharp.Identity for the person who holds a row
model/<Name>.osya virtual policy Manages<Name> the app answers, an entity whose rules name only that policy and the row's owner, an [Event] and the function that raises it
tests/app.osy · tests/model/people.osythe test app — a [Principal], roles, and its answer to the kit's policy
tests/<name>.test.osya [TestFixture] that seeds two people, and two tests that run as each of them — both pass

An integration kit (--http <host>) wraps one HTTP service behind a seam:

filewhat it is
package.osythe manifest — egress "<host>", and how a secret reaches it, stubbed with comments
model/<Name>.osythe SEAM (I<Name>Source), a real client over HTTP, and the function the app calls
tests/model/fake.osya FAKE at the seam, so the tests never call the real service
tests/<name>.test.osyone passing test, against the fake

Both print what to run next as commands that work from the app's folder:

osy test kits/passes/tests     # the kit's own tests, in its test app
osy kits check kits/passes     # whether it composes: it reaches into no sibling kit, and a test shows every seam

It resolves by convention. A project's own kits/* — the nearest kits/ from the project up to its repository's root — is used with no osy.workspace (the file still works, for other layouts). Resolution is by the NAME the kit's package.osy declares, never by the folder. Then, in the app: use Climb.Passes@0; — or, for an integration kit, use Acme.GoogleHealth@0 { egress "api.health.test"; }.

What an integration kit buys, all of it the kit model's: only the kit can reach the host; its credential is a field the app fills from its own secrets; its tests run against the fake; and a second app can use it as it is. osy lint suggests it (reliability-integration-not-in-a-kit, CONSIDER) when an app's own manifest declares egress to a third-party host.

kit or kits?#

osy kit is the UI controls (osy kit Card); osy kits is the packages (osy kits Osysharp.Mail). Each names the other when handed the other's argument, so osy kit Mail says to run osy kits Mail.

See also#

Related

Connecting a kit — each seam with the code that connects it

Prints a kit's composition surface — every event, policy, setting, slot, contract, base, grant, extension member…

Composing kits — what they give together, and what is left for you

Before any code, shows what a set of kits gives together — the bridges that arrive by themselves, every event with its…

Contracts between kits — an interface in a package, and its version

A kit that needs something done declares an ordinary public interface; another kit implements it; the app picks one in…

package.osy

The manifest that makes a git repository a publishable Osy# package. It names the package, says which of the…

Interfaces — a contract several types can satisfy

An `interface` declares what a type must do without saying how — methods and property contracts, and no state. A member…

The Mail kit — send a message, keep a record, pick a sender

`Osysharp.Mail` is the contract between an app that sends mail and whatever carries it. `SendMail` writes the message…

Reading a capability's source

Prints the source of a capability you opted into with `using`. A capability's source ships inside the platform rather…

Osysharp.Ui (the UI kit)

The bundled UI kit — ready-made styled controls like `Button`, the shared design-system vocabularies (`Tone`, `Size`)…