| 1 | # 🤖 suite |
| 2 | |
| 3 | The Oficina suite's shared repository: the sharing doctrine that binds |
| 4 | every app, and the contract module they implement against. |
| 5 | |
| 6 | The suite is a consolidation. Home, Docs, Sheets, memoria and calendar |
| 7 | each keep their own repository, and mail and files arrive later as |
| 8 | wrappers around keymail and slopbox. What they share lives here so no app |
| 9 | owns the rule that binds the others. |
| 10 | |
| 11 | ## What is here |
| 12 | |
| 13 | - `docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md`: the |
| 14 | doctrine. The team is the boundary. Inside it the apps are one thing; |
| 15 | across it, sharing is membership and nothing moves. Read §1 first. |
| 16 | - The contract module, `amadan.net/oficina/suite`: types, golden fixtures |
| 17 | and a thin client for the calls apps make to each other. It has no |
| 18 | dependencies, so the pin chain from an app is one deep. |
| 19 | - `fixtures/`: the golden set. `custody.json` is one case per rung of the |
| 20 | sharing ladder; `hostile_hosts.json` is the list of hostnames a sibling |
| 21 | client must refuse. Both are shared so every app runs the same gate |
| 22 | rather than inventing its own. |
| 23 | - `conformance/`: the harness an app points at its own server half. |
| 24 | |
| 25 | ## How an app uses it |
| 26 | |
| 27 | An app spec cites the doctrine and lists the rulings it consumes. It never |
| 28 | restates one. |
| 29 | |
| 30 | In code, an app *will* import the module, mount the three sibling routes on |
| 31 | a handler, and run the harness against that handler in CI. **Not yet — see |
| 32 | Status: the import path does not resolve, so the module has no consumers and |
| 33 | cannot get one.** The shape it will take: |
| 34 | |
| 35 | ```go |
| 36 | func TestSuiteConformance(t *testing.T) { |
| 37 | conformance.Run(t, myapp.SuiteRoutes()) |
| 38 | } |
| 39 | ``` |
| 40 | |
| 41 | For the calls it makes, it takes a client that cannot leave its own team: |
| 42 | |
| 43 | ```go |
| 44 | c := suite.NewSiblingClient("acme", nil) // refuses another team's label, |
| 45 | // and never follows a redirect |
| 46 | ``` |
| 47 | |
| 48 | ## What is built, and what is not |
| 49 | |
| 50 | Built: S1 the change feed, S2 authorization with its total decoder, S3 |
| 51 | content, the typed asker subject, the team-bound client, and gates 1 to 4. |
| 52 | That is the doctrine's §12 step 2, and it does not wait on the home. S6 is |
| 53 | step 4, |
| 54 | after memoria's slice 1 and before its slice 2; it is specified and has no |
| 55 | fixture yet. |
| 56 | |
| 57 | Not built, deliberately: the server half — that arrives with the first app |
| 58 | that serves one, and the fixtures get corrected by the second, which is the |
| 59 | point of §10. Nor call 7, call 8, the roster or anything needing an |
| 60 | assertion; those are §12 steps 4 and 5 and they wait on the home's identity |
| 61 | layer. |
| 62 | |
| 63 | ## Status |
| 64 | |
| 65 | **The doctrine is the status.** Read its own status block rather than this |
| 66 | one — it is at `docs/superpowers/specs/2026-09-05-suite-sharing-doctrine.md` |
| 67 | and it says which revision it is, who has reviewed it, and what is |
| 68 | deliberately unanswered. |
| 69 | |
| 70 | This section used to repeat all of that and went stale within a day: it |
| 71 | still said revision 4 and three review passes when the doctrine was at 7, |
| 72 | which is precisely the drift §2 diagnoses and §10 exists to stop, committed |
| 73 | by this repository's own README about its own document. So it stopped |
| 74 | repeating it. |
| 75 | |
| 76 | What belongs here, because it is about the repository rather than the |
| 77 | doctrine: **the contract module cannot currently be imported.** |
| 78 | |