# Finding a kit, and learning one from its tests

> 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.

<!-- id: local-finding-a-kit · area: local · stability: stable · html: https://osysharp.com/reference/local/finding-a-kit/ -->

## Summary        {#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      {#signature}

```console
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    {#description}

### What is in scope, and who implements what?    {#listing}

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:

```console
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?    {#wiring}

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:

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

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

```console
  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?    {#front-door}

`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](https://osysharp.com/reference/local/connecting-a-kit/)).

```console
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`    {#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.

```console
$ 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`.

```console
$ 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`):

```console
$ 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    {#none-fits}

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:

```console
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](https://osysharp.com/reference/project/package/)).

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

| file | what it is |
|---|---|
| `package.osy` | the manifest — no egress, no secrets; it builds on `Osysharp.Identity` for the person who holds a row |
| `model/<Name>.osy` | a `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.osy` | the test app — a `[Principal]`, roles, and its answer to the kit's policy |
| `tests/<name>.test.osy` | a `[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:

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

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

```console
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`?    {#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       {#see-also}
- [Connecting a kit — each seam with the code that connects it](https://osysharp.com/reference/local/connecting-a-kit/) — each seam of a kit, with the test app code that connects it
- [Composing kits — what they give together, and what is left for you](https://osysharp.com/reference/local/composing-kits/) — `osy compose`: what kits give together and what is left for you
- [Contracts between kits — an interface in a package, and its version](https://osysharp.com/reference/project/contracts/) — what a contract between kits is, and how its version is checked
- [Interfaces — a contract several types can satisfy](https://osysharp.com/reference/class/interfaces/) — interfaces, and default members
- [The Mail kit — send a message, keep a record, pick a sender](https://osysharp.com/reference/ui/mail-kit/) — a contract kit in full (`IMailSender`)
- [Reading a capability's source](https://osysharp.com/reference/local/reading-a-capability/) — `osy source`, a capability's source
- [Osysharp.Ui (the UI kit)](https://osysharp.com/reference/ui/kit/) — `osy kit`, the UI controls
