@agentshouse/skills 0.1.0-ca20ec24d3b6c → 0.1.0-ca3affce380c3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/agent-instructions.json +1 -1
- package/guide/{routines → agent-functions}/work-on-ticket.md +2 -2
- package/guide/apps/data.md +4 -3
- package/guide/apps/example-function.md +1 -1
- package/guide/apps/functions.md +4 -4
- package/guide/apps/local-first.md +8 -7
- package/guide/apps/local-query-results.md +8 -2
- package/guide/apps/local-query.md +12 -5
- package/guide/apps/page.md +1 -0
- package/guide/apps/room-drafts.md +1 -1
- package/guide/apps/runtime-api.md +2 -1
- package/guide/apps/runtime-files.md +2 -2
- package/guide/apps/runtime-refusals.md +4 -0
- package/guide/apps/runtime.md +1 -1
- package/guide/apps/search.md +22 -0
- package/guide/apps/verify.md +1 -1
- package/guide/index.md +1 -1
- package/guide/owner-rules.md +5 -3
- package/guide/reference/agent-function.md +17 -0
- package/guide/reference/app-manifest.md +1 -1
- package/guide/reference/triggers.md +7 -9
- package/guide/sites/build.md +1 -1
- package/guide/sites/entry-data-contract.md +1 -1
- package/guide/sites/runtime.md +1 -1
- package/guide/skills/house-onboarding.md +1 -1
- package/guide/skills/prototype-app.md +4 -4
- package/owner-rules-earlier.json +2 -1
- package/package.json +1 -1
- package/set/code-review/SKILL.md +1 -1
- package/set/codebase-design/DESIGN-IT-TWICE.md +1 -1
- package/set/grilling/SKILL.md +1 -1
- package/set/implement-spec/SKILL.md +4 -2
- package/set/improve-codebase-architecture/SKILL.md +1 -1
- package/set/research/SKILL.md +1 -1
- package/set/wayfinder/SKILL.md +1 -1
- package/upstream.json +2 -2
- package/guide/apps/local-search.md +0 -35
- package/guide/reference/routine.md +0 -17
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The Skills agents.house serves its agents, and the texts its agents read.
|
|
|
11
11
|
an App, `sites/` what a Site adds, and `skills/` agents.house's own Skills, `house-onboarding`,
|
|
12
12
|
`prototype-app`, `build-app` and `wizard`, each page carrying the `name` and `description` a
|
|
13
13
|
`SKILL.md` carries.
|
|
14
|
-
`
|
|
14
|
+
`agent-functions/` holds the Agent functions agents.house writes into every Private Room, frontmatter and
|
|
15
15
|
all. `owner-rules` is the Owner rules' text, and `owner-rules-earlier.json` the SHA-256 of every
|
|
16
16
|
earlier text.
|
|
17
17
|
- `agent-instructions.json` is the lines that open an agent's first prompt.
|
package/agent-instructions.json
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
"Work with House through the `house` CLI: run `house --help`, and `house open_guide` opens `index`, the root page of House's guides and formats.",
|
|
5
5
|
"To give the User a file, upload it with `house upload` and link it; for an App, a Site or a website, follow the Guide from `index` with `house open_guide`. Never start a server.",
|
|
6
6
|
"Optional per-Room instructions: `/rooms/private/rooms/<handle>/AGENTS.md`.",
|
|
7
|
-
"For another Agent's help, `
|
|
7
|
+
"For another Agent's help, `send_agent_function_request` naming one of the Room's Agent function cards under `house/agent-functions/`, or the Agent and what you ask, as its Trust level there allows; its mail comes back into this conversation."
|
|
8
8
|
]
|
|
9
9
|
}
|
|
@@ -5,11 +5,11 @@ input: The ticket to work on.
|
|
|
5
5
|
|
|
6
6
|
# Work on ticket
|
|
7
7
|
|
|
8
|
-
The
|
|
8
|
+
The Agent function an Agent runs when a person sets a Wayfinding ticket in progress with that Agent as its assignee.
|
|
9
9
|
|
|
10
10
|
Work the one ticket this request names, then stop:
|
|
11
11
|
|
|
12
|
-
1. Open the `wayfinding-ticket` the request names and set its `conversation` to this conversation.
|
|
12
|
+
1. Open the `wayfinding-ticket` the request names and set its `conversation` to the id of this conversation, which House's first message names.
|
|
13
13
|
2. Work it by its `kind`: for `research`, find out and say where you found it; for `prototype`, build the smallest thing that answers its Question; for `grilling`, ask your User until its Question is settled; for `task`, do what it asks.
|
|
14
14
|
3. Write what you found or made in its Answer section and set its `status` to `done`.
|
|
15
15
|
4. Start no other ticket, not even one this ticket unblocks.
|
package/guide/apps/data.md
CHANGED
|
@@ -6,8 +6,9 @@ How an App keeps what it shows as typed documents in the Rooms bound to it.
|
|
|
6
6
|
- Keep each type in its own folder, such as `tasks/`, and read it by `pathPrefix`: a query naming `types` fails with `unknown-type` while no bound Room declares them.
|
|
7
7
|
- Link types with `reference` fields instead of copying values; a query follows them with `hop` and `backlinks`.
|
|
8
8
|
- `app.json` `room_slots` names the Rooms the App needs, each with `access` `read` or `write` and the `types` it uses ([App manifest](reference/app-manifest)). Bind Rooms with `set_app_bindings`; App code cannot bind and reaches only bound Rooms, so it opens `bridge.ceremony('/bind/<app_ref>?slot=<name>')`, the House page where your User binds Rooms, which returns to the App.
|
|
9
|
-
- A bound Room must hold each slot type identically in its own `settings/schema.yaml`, beside any other types, or
|
|
9
|
+
- A bound Room must hold each slot type identically in its own `settings/schema.yaml`, beside any other types, or its binding warns the User who bound it and House tells the App's owner once for that release and slot. Write it with `edit` before binding; `create_room` makes a new Room.
|
|
10
10
|
- Read an existing Room's `settings/schema.yaml` before inventing types. Two Apps over one schema are two interfaces over the same data, and your User's agents read and write those documents too.
|
|
11
|
-
- House's own facts, such as your User's Rooms, come from House acts: name each in `app.json` `house_acts` and call it with `runtime.call('<act>', input)`, such as `runtime.call('rooms/read', {})`. A call needs a connection, so offline show what the bound Rooms hold. A slot holding the Private Room brings its Triggers and none of the other documents House writes under its `house/`.
|
|
11
|
+
- House's own facts, such as your User's Rooms and the Space each is in, come from House acts: name each in `app.json` `house_acts` and call it with `runtime.call('<act>', input)`, such as `runtime.call('rooms/read', {})`. A call needs a connection, so offline show what the bound Rooms hold. A slot holding the Private Room brings its Triggers and none of the other documents House writes under its `house/`.
|
|
12
|
+
- `shares/create` lets a person into Rooms and Apps: its answer's `added` is `true` when their _Who can add me_ setting admits your User, so they are in at once, and `false` when the Share waits for their Accept.
|
|
12
13
|
- What no browser can do, such as acting when documents change or at a set time, is a Trigger (`create_trigger`) that notifies or runs one of the App's [functions](apps/functions) on House's servers; what it writes reaches the App at once.
|
|
13
|
-
- A Trigger may also start
|
|
14
|
+
- A Trigger may also start an [Agent function](reference/agent-function) on an Agent of your User.
|
|
@@ -64,4 +64,4 @@ export default async (event, house) => {
|
|
|
64
64
|
};
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
A lead the CRM delivers again finds its decision and calls nothing. Two leads that arrive together run one after another, so the second counts the first's decision and goes to another manager when two are tied. Each count is a query's `total`, so a long shift is never cut at a page. A failed call fails the run with the CRM's answer
|
|
67
|
+
A lead the CRM delivers again finds its decision and calls nothing. Two leads that arrive together run one after another, so the second counts the first's decision and goes to another manager when two are tied. Each count is a query's `total`, so a long shift is never cut at a page. A failed call fails the run with the CRM's answer; the next lead still runs.
|
package/guide/apps/functions.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
How an App declares a function that House runs on its servers when a Trigger fires, with no page open and no model.
|
|
4
4
|
|
|
5
5
|
- **Declare.** `app.json` lists `functions`, each `{"name": "assign lead", "entry": "functions/assign.js"}` ([App manifest](reference/app-manifest)). The entry is one ES module whose default export is the function and which imports nothing, so your build bundles each function, with all it imports, into `dist/`, such as `esbuild src/assign.ts --bundle --format=esm --outfile=dist/functions/assign.js`. A publish is refused whole for an entry that is missing, imports, has no default export or is over <function-size>, for a name used twice, and for more than <release-functions> functions.
|
|
6
|
-
- **Run.** A Trigger's `function` action names the App and one of its functions ([Triggers](reference/triggers)). House runs the App's latest release when the Event's turn comes, one Event of a Trigger after another, so a run reads what the run before it wrote.
|
|
7
|
-
- **The event.** `export default async (event, house) => {}` receives `kind`, the condition's `on`, with `room`, `trigger`, `id`, `moment` and what matched: `path` and `reference`, a document change's `transition`, an arriving item's `item` with its `headers` and raw `body` text, a dated document's `document`, a delivered notification's `notification`, the facts of its web document, or a message's `message`, its `sender` and its request's `
|
|
8
|
-
- **The App API.** `house` is the page's App API over the Trigger's Room alone: `query(q)` ([Local Query](apps/local-query)), `saveParts(room, parts)`, `call(name, input)` for the Room acts, each by its House act name, such as `records/read` and `rooms/members`, and `callExternal(request)`. `query` and `callExternal` reject a refusal with its `code` for the function to catch, while `call` answers it as a result whose `kind` is `rejected` and `saveParts` as `outcome: 'refused'`, as on the page, so check what they answer. Its writes are the App's, by `App <App> (function <function>)`, fire Triggers, its own included, and count against the Room owner's Write limit.
|
|
6
|
+
- **Run.** A Trigger's `function` action names the App and one of its functions ([Triggers](reference/triggers)). House runs the App's latest release when the Event's turn comes, one Event of a Trigger after another, so a run reads what the run before it wrote. Runs share a fixed number of slots on House's servers, and a run waits, its action `waiting`, until one is free.
|
|
7
|
+
- **The event.** `export default async (event, house) => {}` receives `kind`, the condition's `on`, with `room`, `trigger`, `id`, `moment` and what matched: `path` and `reference`, a document change's `transition`, an arriving item's `item` with its `headers` and raw `body` text, a dated document's `document`, a delivered notification's `notification`, the facts of its web document, or a message's `message`, its `sender` and its request's `agent_function`, never its words, with the document that request was started for as `path` and `reference`. `Date` reads `moment`. The return value is ignored, so write what you keep into the Room.
|
|
8
|
+
- **The App API.** `house` is the page's App API over the Trigger's Room alone: `query(q)` ([Local Query](apps/local-query)), which reads only the documents of the `types`, folders (`folder`, `pathPrefix`) and `paths` it names and answers over them alone, so the rest of a large Room is never read: name the `types` or `pathPrefix` of its `hop` and `backlinks` too, and select its parts, since a query or `hop` or `backlinks` naming none, or selecting `backlinks` or `'all'`, is refused `invalid-query`, `saveParts(room, parts)`, `call(name, input)` for the Room acts, each by its House act name, such as `records/read` and `rooms/members`, and `callExternal(request)`. `query` and `callExternal` reject a refusal with its `code` for the function to catch, while `call` answers it as a result whose `kind` is `rejected` and `saveParts` as `outcome: 'refused'`, as on the page, so check what they answer. Its writes are the App's, by `App <App> (function <function>)`, fire Triggers, its own included, and count against the Room owner's Write limit.
|
|
9
9
|
- **Reach.** A run holds no `fetch`, timer, file, `console` or `Intl`: keep times as ISO instants, or convert a local time by its UTC offset yourself. `callExternal` takes the request `bridge.callExternal` takes and is the one way out, to an origin the release's `external_origins` lists; naming its Room, `room_ref: event.room`, carries that Room's Service key, which the function never sees, or is refused `service_key_not_found` naming the `origin`. Its calls count against the Room owner's `external_calls` limit.
|
|
10
10
|
- **Never repeated.** A run that started is never run again, even when it failed after a call reached the service. Send `event.id` as the service's idempotency key where it takes one, and name what you write by what the event carries, so a second delivery of the same thing finds it.
|
|
11
|
-
- **Failure and limits.** A throw settles the run `failed` with `function_failed` and its text, which `list_trigger_events` shows and
|
|
11
|
+
- **Failure and limits.** A throw settles the run `failed` with `function_failed` and its text, which `list_trigger_events` shows and the panel shows the Room's owner, with no notification; the next Event still runs. A run ends `failed` at <function-time>, at <function-memory> or past <function-calls> external calls: `time_cap_reached`, `memory_cap_reached` or `external_call_cap_reached`.
|
|
12
12
|
|
|
13
13
|
Next: [Example: assign a lead](apps/example-function)
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
# Local first
|
|
2
2
|
|
|
3
|
-
How an App reads and writes its Rooms on the device, offline
|
|
3
|
+
How an App reads and writes its Rooms on the device, offline too.
|
|
4
4
|
|
|
5
|
-
- The runtime holds every bound Room on the device and streams in every change
|
|
6
|
-
- Draw from `observe` at once, with no
|
|
5
|
+
- The runtime holds every bound Room on the device and streams in every change from any device, App or agent. `query` and `observe` answer from the device and never wait on the network; `search` asks House ([Search rules](apps/search)).
|
|
6
|
+
- Draw from `observe` at once, with no request of your own; an observation hears each later change by itself, so never poll, refetch or subscribe. An answer is `{ pending }` while a Room it reads has not reached this device or been prepared on it: draw the page without those documents; keep the empty state for a complete answer. `rooms` answers once the runtime knows the Rooms the page reaches. Offer binding a Room when it is empty while the sync status's `providerUnreachable` is false and `observe('connection-refusal')` answers `null`; any other empty list says only that nothing can be listed yet.
|
|
7
|
+
- Show no loading state: draw the last state the device holds. A wait shows only after an act the User pressed whose answer comes from outside the stream, such as `bridge.callExternal`, until it arrives.
|
|
7
8
|
- Write optimistically: write a Room Draft, then `save`. The change shows in every query at once as a Pending Change and reaches House when it can. `save` refuses at once, with its `code`, a change that is `stale`, `changed-underneath` or against the schema: say so and keep the form.
|
|
8
9
|
- Show what the runtime reports: `observe('queue', room)` lists changes House has not confirmed yet, and `observe('sync-status')`'s `attentionRequired` lists each change House refused, returned as a Room Draft with why, to save again or discard.
|
|
9
10
|
- Keep what the User types in the Room Draft (`roomDraft.write`) as they type; it survives a new release replacing the page, a reload and another tab. Draw a form again only when its page changes, never on a streamed change.
|
|
10
11
|
- A document has one Room Draft, shared by every tab and App, and `save` takes all of it. A write replaces every part and names the revision it read, so write onto the parts it holds; one from an older revision answers `stale` with the current Room Draft: show that one. A field part names as its `base` the value the page showed, `{ value }`, or `null` when the document has no such field.
|
|
11
12
|
- Save a change the User does not type, such as a checkbox, with `saveParts(room, [{ path, parts }])`: it writes no Room Draft, so the one another tab holds stays.
|
|
12
|
-
- Offline, all of this works and saves wait
|
|
13
|
-
- A Room your User holds at the Open level locks once the browser has been offline 2 minutes, and from the moment it opens until House confirms the access. While it is locked, `rooms` lists it `locked: true`, a query
|
|
14
|
-
- An outside service is reached only at the origins `app.json` lists in `external_origins`, directly or through `bridge.callExternal`,
|
|
13
|
+
- Offline, all of this works and saves wait; `bridge.onStatus` reports `offline`: say so quietly.
|
|
14
|
+
- A Room your User holds at the Open level locks once the browser has been offline 2 minutes, and from the moment it opens until House confirms the access. While it is locked, `rooms` lists it `locked: true`, a query that does not name it leaves it out, one naming it answers `{ pending }` with `awaits: 'unlock'`, and every other read and write naming it, `save` included, answers the code `room-locked` and keeps nothing. Draw it locked until it unlocks and keep what the User types in the page; what was saved before stays and is sent then.
|
|
15
|
+
- An outside service is reached only at the origins `app.json` lists in `external_origins`, directly or through `bridge.callExternal`, sent by House's server. A call that names a Room bound to the App in a `write` slot with `room_ref` carries that Room's Service key for the origin, which the page never sees; any other call carries no key. A call through a Room that holds no key for the origin is refused `service_key_not_found` naming the `origin`: tell the person that the Room's owner or a manager enters the key under Service keys in the Room's Info, since no App opens that page.
|
|
15
16
|
- The page's own storage is a disposable cache, the Room the only durable home of the App's data; handle any code or value you do not know through `code` and `retryable`, which every refusal and failure carries.
|
|
16
|
-
- React: `useObserve` and `useSave` from `@agentshouse/mdruntime/react`. react-admin and Refine: the data providers of `@agentshouse/mdruntime-families`. The whole client
|
|
17
|
+
- React: `useObserve` and `useSave` from `@agentshouse/mdruntime/react`. react-admin and Refine: the data providers of `@agentshouse/mdruntime-families`. The whole client: [The App runtime](apps/runtime).
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# Local Query changes, order and results
|
|
2
2
|
|
|
3
|
-
How a Local Query answers each change, orders, groups and pages its result, and fails.
|
|
3
|
+
How a Local Query answers text it does not hold and each change, orders, groups and pages its result, and fails.
|
|
4
4
|
|
|
5
|
+
- **Held text.** The device holds every Document's type, fields and links, and the text of
|
|
6
|
+
each one up to House's device text bound and of each larger one read on it. A Document whose
|
|
7
|
+
text it does not hold answers `textHeld: false`, and no selected `markdown`, `sections` or
|
|
8
|
+
`preamble`. A query that selects its text reads it from House while online and keeps it, so
|
|
9
|
+
select `fields` alone for a list that shows no text; offline it answers the mark, and a Save
|
|
10
|
+
that needs that text is refused `transport-uncertainty`.
|
|
5
11
|
- **Change, file ID and revision.** `change` is `{ author, time }` of the Document's head revision
|
|
6
12
|
as the Provider last delivered it: `author` is `{ kind: 'user' | 'inbox', id, name }`, or
|
|
7
13
|
`null` for a write the Provider made itself, and `time` its ISO 8601 change time, or the
|
|
@@ -12,7 +18,7 @@ How a Local Query answers each change, orders, groups and pages its result, and
|
|
|
12
18
|
is Accepted. `revision` is the Provider's head revision, `null` for an unsent creation or edit,
|
|
13
19
|
and moves with an unsent move. A Room's last change is its Document with the latest `time`.
|
|
14
20
|
- **Order and groups.** `orderBy` sorts by `room`, `path`, `type`, `folder` or `{ field }`: missing
|
|
15
|
-
and null first, last with `nulls: 'last'`, then numbers and booleans, then text by `locale` or
|
|
21
|
+
and null first, last with `nulls: 'last'`, then numbers and booleans, then date-times as instants, then text by `locale` or
|
|
16
22
|
code point with `stringSort: 'lexical'`, then lists item by item; ties end with Room, then Path.
|
|
17
23
|
`groupBy` by `room`, `type`, `folder` or `{ field }` answers `{ groups: [{ key, count }] }`,
|
|
18
24
|
counted over every match and ordered by `count` or a key. A sort or group takes only a
|
|
@@ -13,17 +13,24 @@ draw it as loading and observe the query to hear the complete answer. A query is
|
|
|
13
13
|
Types, and `any` names untyped Markdown Documents. `pathPrefix` is a directory: `notes`
|
|
14
14
|
keeps `notes/a.md` and not `notes2/a.md`; `folder` keeps only what lies directly in it.
|
|
15
15
|
`paths` keeps only the Paths it names: read named Documents so, never from a list.
|
|
16
|
+
`fileIds` keeps only the Documents of the `fileId`s it names, wherever they moved, on
|
|
17
|
+
the device alone: an App function refuses it.
|
|
18
|
+
`text` keeps the Documents whose held text contains that phrase, ignoring case, ranking
|
|
19
|
+
nothing; a text the device does not hold never matches.
|
|
16
20
|
- **Fields.** Every frontmatter value is named by JSON Pointer: `/status`,
|
|
17
21
|
`/shipping/city`, `~1` for a `/` and `~0` for a `~` in a key. A segment that meets a
|
|
18
22
|
list applies to every item and a number names one item. `where` names pointers, ANDs
|
|
19
23
|
them, and nests `$and` and `$or`; a pointer through a list holds if any item satisfies
|
|
20
24
|
it. A bare value is equality; the operators are `$eq`, `$ne`, `$in`, `$nin`, `$gt`,
|
|
21
|
-
`$gte`, `$lt`, `$lte`, `$all`, `$size` and `$
|
|
25
|
+
`$gte`, `$lt`, `$lte`, `$all`, `$size`, `$exists` and `$prefix`, which keeps text starting
|
|
26
|
+
with it, case kept. Equality on a list asks if it
|
|
22
27
|
contains the value. Missing, null and empty differ: `$exists: false` keeps a missing
|
|
23
|
-
field, `null` an explicit null, `''` empty text and `$size: 0` an empty list. A
|
|
24
|
-
|
|
28
|
+
field, `null` an explicit null, `''` empty text and `$size: 0` an empty list. A test on a
|
|
29
|
+
missing value is false and `$ne`, `$nin` and `$exists: false` true. A date
|
|
30
|
+
compares in calendar order and never with a date-time. A date-time compares as an instant,
|
|
31
|
+
whatever offset it is written in. A reference compares by the
|
|
25
32
|
Room Path it resolves to. Untyped frontmatter, and a value under `oneOf` or a
|
|
26
|
-
conditional, is plain values. There is no
|
|
33
|
+
conditional, is plain values. There is no regular expression.
|
|
27
34
|
- **Relations.** Within one Room, `hop: { field, types, where, pathPrefix }` keeps Documents
|
|
28
35
|
whose reference at pointer `field`, at any depth, points to a matching Document, and
|
|
29
36
|
`backlinks` with the same keys those a matching Document references, each once. A body
|
|
@@ -38,7 +45,7 @@ draw it as loading and observe the query to hear the complete answer. A query is
|
|
|
38
45
|
return; a Markdown Document also returns its MD Type, and at most its first 1,000
|
|
39
46
|
backlinks: a query on the referring field reads the rest. A non-Markdown Document
|
|
40
47
|
returns only Room, Path and a selected `fileId` and `revision`, a binary Path entry its `binaryFile`
|
|
41
|
-
record too, and a text file its text as a selected `markdown
|
|
48
|
+
record too, and a text file its text as a selected `markdown`. An unselected part is absent. Select only what a screen shows: a list
|
|
42
49
|
that selects the Markdown hears every edit of it.
|
|
43
50
|
|
|
44
51
|
Next: [Local Query changes, order and results](apps/local-query-results)
|
package/guide/apps/page.md
CHANGED
|
@@ -11,3 +11,4 @@ What an App's page draws and how it behaves in the browser.
|
|
|
11
11
|
- Long names truncate with an ellipsis and show in full on hover.
|
|
12
12
|
- The element is the link: a row, card or name opens its item; no "Open" buttons.
|
|
13
13
|
- Offer installation yourself: `bridge.install()` from a click shows the browser's prompt, or answers `manual` where you show how.
|
|
14
|
+
- Offer a way out where your User expects one: `bridge.ceremony('/sign-out')` signs them out of House and opens sign-in.
|
|
@@ -25,7 +25,7 @@ writing back a Room Draft read before.
|
|
|
25
25
|
|
|
26
26
|
Outcomes follow the Provider's answer. An Accepted Change Batch stays in the queue, drawn
|
|
27
27
|
over the confirmed layer, until that layer reaches the Room Version it was accepted at.
|
|
28
|
-
Conflicted and Rejected,
|
|
28
|
+
Conflicted and Rejected, and a change that no longer applies, each
|
|
29
29
|
leave the queue and return as Room Drafts on their Documents, marked with why: the
|
|
30
30
|
Provider's structured refusal code and fields as returned, not known to have applied, or
|
|
31
31
|
withdrawn locally. The rest of the queue keeps synchronizing, and Sync Status requires
|
|
@@ -6,7 +6,8 @@ App code reaches the MD Runtime through the remote client. The surfaces are Room
|
|
|
6
6
|
Search, Observe, Types, Room Draft, Save, History, Resolve, binary files, Call,
|
|
7
7
|
Reached, Log, the reported persistence answer, reauthorization, conversation, the chat
|
|
8
8
|
draft, and Presentation State. Sync Status, readiness, the Service Mode, the storage-loss
|
|
9
|
-
statement, the deployment facts
|
|
9
|
+
statement, the deployment facts, the signed-in member and the count of authority changes
|
|
10
|
+
are observations only.
|
|
10
11
|
|
|
11
12
|
The host attaches each port with its reach, and every member and observation answers
|
|
12
13
|
within it: a Room outside it answers as one the replica does not hold, Rooms lists only
|
|
@@ -38,8 +38,8 @@ or remove one as a Document, its bytes following.
|
|
|
38
38
|
mediaType }`, the bytes a `ReadableStream` of parts of at most a mebibyte, which the
|
|
39
39
|
browser keeps for later reads, offline too.
|
|
40
40
|
|
|
41
|
-
Rooms, Query,
|
|
42
|
-
|
|
41
|
+
Rooms, Query, Observe, Types, Room Draft, Save, and Reached never touch the network;
|
|
42
|
+
Search, History, a binary file's attach and download, a file shown or read that the
|
|
43
43
|
browser does not keep, and Call go to the Provider through the Adapter.
|
|
44
44
|
Call takes the Action names the Adapter declares, any other answering `undeclared`. A Call
|
|
45
45
|
result whose effect the Provider places in a Room carries `at`, that Room and Room
|
|
@@ -17,3 +17,7 @@ learns it: the User the Provider signed in, `{ kind: 'user', id, name }` like a
|
|
|
17
17
|
until the Provider first names one, and where it names none. It is kept
|
|
18
18
|
with the Local Replicas, answers offline once known, and every port hears the next member
|
|
19
19
|
named; signing out answers `null`, then ends the observation with the authorization.
|
|
20
|
+
`observe('authority-changes')` answers how many times House told the runtime that what its
|
|
21
|
+
User may see changed, a Room's members, a Share or an offer among them, and hears each next
|
|
22
|
+
one, never a reconnection, so a view that read such a fact through Call reads it again when
|
|
23
|
+
the count moves.
|
package/guide/apps/runtime.md
CHANGED
|
@@ -7,7 +7,7 @@ change it, build, and push. They are documentation, not enforcement.
|
|
|
7
7
|
|
|
8
8
|
- [The MD Runtime API](apps/runtime-api)
|
|
9
9
|
- [Local Query rules](apps/local-query)
|
|
10
|
-
- [
|
|
10
|
+
- [Search rules](apps/search)
|
|
11
11
|
- [Room Draft, Save, and outcome rules](apps/room-drafts)
|
|
12
12
|
- [Content safety and launch promises](apps/content-safety)
|
|
13
13
|
- [App code takes the client its host hands it and never constructs a Runtime](apps/client)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Search rules
|
|
2
|
+
|
|
3
|
+
How `search` finds Documents by words through House, and how a page finds them offline.
|
|
4
|
+
|
|
5
|
+
`search({ text, rooms })` asks House's search, the one Agents use, once and online: there
|
|
6
|
+
is no observed search and the device keeps no full-text index. `rooms` names Room locators
|
|
7
|
+
and defaults to every Room the page binds; the search never leaves those Rooms, and finds
|
|
8
|
+
words in every Document there, a large one the device never read included.
|
|
9
|
+
|
|
10
|
+
- **Results.** `{ kind: 'found', documents }`, ranked by House, at most 20 hits. A hit names
|
|
11
|
+
its `room` and `path`, and a text hit its first `line` and a short `snippet` of it. Read a
|
|
12
|
+
hit's Document with `query` naming its `paths`: a text the device does not hold arrives
|
|
13
|
+
from House.
|
|
14
|
+
- **Offline.** Without a connection `search` answers `{ kind: 'offline' }`, and where no
|
|
15
|
+
House search is reached, `{ kind: 'undeclared' }`. Find Documents then with a
|
|
16
|
+
[Local Query](apps/local-query): by Path, name, field values and `text`, a phrase its held
|
|
17
|
+
text contains. That filter ranks nothing and never finds a text the device does not hold.
|
|
18
|
+
- **Refusals.** House's refusal answers as `kind: 'rejected'` with its `code`, and a lost
|
|
19
|
+
answer as `transport-uncertainty`: say the search failed and keep what the page shows.
|
|
20
|
+
- **Conversations.** `search` finds no message. Inside one conversation
|
|
21
|
+
`conversation.read(conversation, { text })` answers its messages that contain a phrase,
|
|
22
|
+
in windows as any read of it.
|
package/guide/apps/verify.md
CHANGED
|
@@ -13,4 +13,4 @@ After it:
|
|
|
13
13
|
- `list_apps` names the App's `published_release`, whose `revision` is the `main` commit it was built from; a clone, a fetch and `git ls-remote` see `main` alone.
|
|
14
14
|
- Bind the Room once with `set_app_bindings`.
|
|
15
15
|
- See it yourself: sign your own browser in with `create_sign_in_code`, then open the App's url.
|
|
16
|
-
- Give your User its address. An open page keeps the release it loaded and
|
|
16
|
+
- Give your User its address. An open page keeps the release it loaded, and its next load made online runs the release you published; nothing asks them to update.
|
package/guide/index.md
CHANGED
|
@@ -8,5 +8,5 @@ While your User's My Room, which `list_rooms` marks, holds no `wayfinding-map`,
|
|
|
8
8
|
|
|
9
9
|
- `apps/`, building an App over your User's Rooms: [Build an App](apps/build), and the functions House runs for it when a Trigger fires: [App functions](apps/functions).
|
|
10
10
|
- `reference/`, the formats House reads: [Access level](reference/access-level), [App manifest](reference/app-manifest), [Schema](reference/schema) and [Triggers](reference/triggers).
|
|
11
|
-
- `
|
|
11
|
+
- `agent-functions/`, the Agent function the panel writes for My Room for each Agent it makes, naming that Agent, written as [Agent function](reference/agent-function) says: [Work on ticket](agent-functions/work-on-ticket).
|
|
12
12
|
- `skills/`, House's Skills: [Prototype an App](skills/prototype-app).
|
package/guide/owner-rules.md
CHANGED
|
@@ -23,9 +23,11 @@ The skills are already set up; never run `setup-matt-pocock-skills`.
|
|
|
23
23
|
- Rejected requests (triage): `out-of-scope/<concept>.md`
|
|
24
24
|
- Implementation notes (implement-spec): `projects/<name>/notes/`
|
|
25
25
|
- PR bodies (pr): `projects/<name>/prs/<branch>.md`
|
|
26
|
-
- Wayfinding map: one `wayfinding-map` document, `projects/<name>/map.md
|
|
27
|
-
|
|
28
|
-
`projects/<name>/map/NN-<slug>.md
|
|
26
|
+
- Wayfinding map: one `wayfinding-map` document, `projects/<name>/map.md`
|
|
27
|
+
with `type: wayfinding-map` in its frontmatter, and its tickets,
|
|
28
|
+
`wayfinding-ticket` documents in order as `projects/<name>/map/NN-<slug>.md`
|
|
29
|
+
with `type: wayfinding-ticket`, since House knows a map or a ticket only by
|
|
30
|
+
its `type`: a ticket carries its `title`, its
|
|
29
31
|
`map` (the map's path), its `kind` (its type) and `blocked_by` (its
|
|
30
32
|
blockers); a claim sets `status: in-progress` and the
|
|
31
33
|
`assignee`, and a resolution writes the Answer and sets `done`, or
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Agent function
|
|
2
|
+
|
|
3
|
+
How an Agent function document in the Private Room is written, one for each Room it serves.
|
|
4
|
+
|
|
5
|
+
Agent function, `/rooms/private/rooms/<room-handle>/agent-functions/<name>.md`, serving the Room `<room-handle>` names: `description`; `input`, a text saying what to send, empty if nothing; and `agent`, the identifier of the one of your User's current Agents it starts. The body is the instruction, which never leaves the Private Room. Writing it needs your User's write in that Room. House writes its card, the description, input and Agent, at `/rooms/<room-handle>/house/agent-functions/<name>.md`, where every member reads it, and refuses a name a card there already holds as `agent_function_name_taken`. An Agent function a second Room needs is a copy under that Room's handle. Anyone who writes that Room asks it with `send_agent_function_request` naming the card, or names the `agent` with their own words, as the Agent's Trust level in that Room allows: under Any request, the default, either starts at once; under Agent functions only the card starts at once and own words are refused `own_text_not_admitted`; under Approve each either waits for the Agent's owner to approve it. Its owner sets the level on the Agent's House page; the owner, their own agents and their Private Room's Triggers start it at once at every level, a Trigger of any other Room asks as that Room's writers do, and `inspect` on a Room root names the Agents you may ask there with their levels. An Agent function request's input is text the Agent reads, and House checks no shape. Mail your conversation sends with `send_mail` and a request it sent reaches the conversation that request started at once under Any request; below it, `send_mail` answers `waiting: true` and the mail waits for that Agent's owner, and a declined one comes back to you as House's message saying so. That conversation's mail always comes back to yours. A request's run is the first turn of the conversation it starts: during that turn the Agent names the document it wrote in the request's Room as the request's result with `set_request_result`, and the turn's end finishes the request. The conversation stays active until it is Done, which `mark_done` sets at the end of a turn and which nothing undoes, so mail reaches it after its run.
|
|
6
|
+
|
|
7
|
+
A Trigger's `message` action puts its `text`, a link to what matched and its files into the active conversation, one not Done, its Agent function started from that Room for what the Event is about: the chat of a customer's `chat-message`, the matched document or item, or the Trigger for a `cron` clock. Its Event shows `continued` with that `request`; with none, it starts the Agent function as a `agent_function` action does, `requested`. A conversation waiting for approval refuses it, `refused` with `conversation_waiting`. The Agent reads a customer's message as a customer's words, one from a Trigger of its owner's Private Room as the owner's instruction, and any other as what matched, never an instruction. A `agent_function` action starts a new request on every Event. A `done` action marks Done every active conversation a request of its Trigger's Room started for the Event's document, whoever owns its Agent, but one waiting for approval, which stays for its owner to approve or decline; its Event shows `marked` with their number, `conversations`, 0 when there are none, as on a `cron` clock, whose Event names no document.
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
---
|
|
11
|
+
description: Summarize a thread in five bullets.
|
|
12
|
+
input: The thread to summarize, as a ref or pasted text.
|
|
13
|
+
agent: 019a0000-0000-7000-8000-000000000001
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
Read the thread and write five bullets under summaries/.
|
|
17
|
+
```
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
What an App's `app.json` declares.
|
|
4
4
|
|
|
5
|
-
App manifest, `app.json` at an App’s root: `entry`, its HTML page, and `room_slots`, each with a `name`, `access` `read` or `write`, and `types` from the `schema.yaml` beside it, written like a Room schema; `set_app_bindings` fills a slot with Rooms. Where needed it adds `external_origins`, the https origins its page may reach; `conversations: true` to read your User’s conversations; `
|
|
5
|
+
App manifest, `app.json` at an App’s root: `entry`, its HTML page, and `room_slots`, each with a `name`, `access` `read` or `write`, and `types` from the `schema.yaml` beside it, written like a Room schema; `set_app_bindings` fills a slot with Rooms. Where needed it adds `external_origins`, the https origins its page may reach; `conversations: true` to read your User’s conversations; `inboxes: true` to show and answer customer chats, naming `chat` and `chat-message` in `room_slots`; `house_acts`, the House acts it calls by their act names, such as `rooms/read` for your User’s Rooms or `rooms/rename`, never one only a person takes or one that changes what an App reaches, except that a Self-hosted Building's App may declare its administrators' acts, `building/people/read`, `building/people/add`, `building/people/remove`, `building/administrators/name`, `building/administrators/take`, `building/factor/require`, `building/factor/stop-requiring` and `building/factor/reset`, which its page calls in an administrator's browser once it has asked them to confirm each; `functions`, each a `name` and the `entry` file a Trigger runs on House's servers ([App functions](apps/functions)); and `theme_colour`, `#rrggbb`. A User who does not own the App is asked once to allow the House acts it declares, and again only for those a later release adds; a declined one is refused. Nobody is asked about an act they have no right to use, and it is refused them unasked, the App's owner included: the administrators' acts `not_building_administrator` to anyone who is no administrator, and `shares/create`, `rooms/create` and the `rooms/access/` acts `operation_denied` to anyone who manages no Room but their Private Room. [Build an App](apps/build) shows how its page works.
|
|
6
6
|
|
|
7
7
|
```json
|
|
8
8
|
{
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
# Triggers
|
|
2
2
|
|
|
3
|
-
How a Trigger's `condition` and `actions` are written for `create_trigger` and `edit_trigger`.
|
|
3
|
+
How a Trigger's `condition` and `actions` are written for `create_trigger` and `edit_trigger`; House writes a Room's Triggers as read-only `house-trigger` documents at `house/triggers/<trigger>.md`.
|
|
4
4
|
|
|
5
|
-
A Room's
|
|
5
|
+
`condition`: When it fires, e.g. `{on: "documents", scope: {type: "task"}, mode: "starts_matching", new_documents: false, match: {path: "/fields/status", eq: "review"}}`; for a folder's new document, `{on: "documents", scope: {folder: "/answers"}, mode: "starts_matching", new_documents: true, match: {all: []}}`; for any edit of a document, `{on: "documents", scope: {document: "/plan.md"}, mode: "field_changes", field: "/text"}`; `moved_between_rooms: true` fires only on a document moved between Rooms, `false` on any other change; for items, `{on: "items", address: "<address>", match: {path: "/json/ref", eq: "refs/heads/main"}}`. A clock is `{on: "clock", cron: "0 9 * * 1", time_zone: "Europe/Berlin"}`, or a day before each task is due: `{on: "clock", scope: {type: "task"}, date: "/fields/due", days: -1, at: "09:00", time_zone: "Europe/Berlin", match: <test>}`. In your Private Room alone, a notification to you: `{on: "notifications", match: {path: "/fields/kind", eq: "share-offer"}}`, testing its `house-notification` document's `/fields/kind` and its value fields, `match` optional; such a Trigger holds no `notify` action. A `/fields/…` or `/members/…` path reads only what the document's own type declares: a document with no `type` has no fields, whatever its front matter holds, so give it a `type` naming one in `settings/schema.yaml` that declares the field.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A message into a conversation the Room's [Agent function request](reference/agent-function) started: `{on: "messages", match: {path: "/sender", eq: "requester"}}`, testing `/sender`, `/agent_function` and `/path`, the request's document, never its words.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
`actions`: a `notify` action names whom it reaches, e.g. `{kind: "notify", title: "Ready", text: "A task waits.", to: {users: ["<User id>"], fields: ["/members/assignee"], roles: ["owner"]}}`, Users by id, the member a field of the matched document names, and every holder of a Schema Role; a `function` action names an App and one function its latest release declares, e.g. `{kind: "function", app: "a_…", function: "assign lead"}`, which you may add only for an App you hold with this Room in a write slot of your bindings; [App functions](apps/functions) says how House runs it. `list_trigger_events` shows each run `ran` with its `release`, or `failed` (`function_failed`, its `error`), never run again, or `function_not_started` while House tries, `final` after its last try.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
In your Private Room alone, a `function` action naming no `app` runs one of your Environment functions ([wizard](skills/wizard)), e.g. `{kind: "function", function: "nightly backup"}`; elsewhere, or for one you do not hold, it is refused `environment_function_not_found`. Kit runs its script with no terminal, the Event as JSON in the file `HOUSE_INPUT` names, one Event at a time. `list_trigger_events` shows each run `ran` or `failed` (`function_failed`, its `error`), never run again, with its `run`, `exit_code` and `output`; a Kit not connected at its turn reads `no_target` with `environment_offline`, never run later.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
A Trigger acts on at most <trigger-events-per-hour> Events in each UTC hour, across its versions, skipped and failed ones not counted; `list_trigger_events` shows the first past the number `missed: "skipped"` with `code: "cap_reached"`, which starts nothing, and none later that hour; the Trigger shows as failing. A Trigger whose own actions make what it fires on stops there, so narrow its condition.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
A `routine` action starts a [Routine](reference/routine) by its card, `{kind: "routine", card: "<name>", payload: "<input>"}`; `{kind: "message", card: "<name>", text: "<text>"}` messages it. No such card is refused `routine_not_found`, a retired one `routine_retired`.
|
|
15
|
+
`{kind: "agent_function", card: "<name>", payload: "<input>"}` starts an [Agent function](reference/agent-function) and `{kind: "message", card: "<name>", text: "<text>"}` messages it; no such card is refused `agent_function_not_found`, a retired one `agent_function_retired`. `{kind: "done"}` marks Done each active conversation the Room's Agent function requests started for the Event's document, `marked` with their number.
|
package/guide/sites/build.md
CHANGED
|
@@ -10,7 +10,7 @@ Build it as [Build an App](apps/build) says, with what follows. Anything private
|
|
|
10
10
|
- A Site is public: anyone opens it, nobody signs in, and every visitor reads its bound Rooms, which must be your User's own and never the Private Room.
|
|
11
11
|
- Everything it shows lives as documents in its Room; put its demo data there, never in code.
|
|
12
12
|
- A visitor's edits stay in their browser and never reach House: write them with `save` as an App does.
|
|
13
|
-
- To receive messages from visitors, name an Intake Room with `set_site_intake_room` and send each with `runtime.call('site/submission', { content })`, online only.
|
|
13
|
+
- To receive messages from visitors, name an Intake Room with `set_site_intake_room` and send each with `runtime.call('site/submission', { content })`, online only. `content` is one Markdown document the Intake Room's `settings/schema.yaml` accepts, and House adds its `site` and `received` fields. The call answers `{ kind: 'action', result: { submitted: true } }` once House stored it. Anything else stored nothing: `{ kind: 'rejected', code }` says why, such as `bot_check_failed` for a browser that failed the bot check, as automated test browsers do, `site_intake_room_absent`, `invalid_documents` with the `path`, `rule` and `field` it broke, `submission_reserved_key`, `submission_too_large`, `limit_reached` or `cap_reached`. So thank the visitor only for `submitted`, and otherwise show that nothing was sent.
|
|
14
14
|
- `describe_site` sets the title and description search engines show.
|
|
15
15
|
- With `google_analytics_id` set by `describe_site`, House measures a visitor only after they accept. While `bridge.onStatus` reports `analytics.measures`, ask them on the Site unless `analytics.choice` holds their answer, report it with `bridge.analyticsConsent('accept')` or `bridge.analyticsConsent('decline')`, and keep a way on every page to decline later, which reloads the page unmeasured.
|
|
16
16
|
- See it yourself: open its url, which needs no sign-in.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
How a Site App's `entry_data` has House serve each route as HTML from Rooms.
|
|
4
4
|
|
|
5
|
-
Entry Data Contract, `entry_data` in a Site App’s `app.json`: House serves each route as HTML from your Rooms before the App starts, running none of your code. Each of `routes` has a `path` (`/`, `/posts/:slug`), optional `query` (functional `?name` parameters with defaults), `queries` (Local Query objects with `types` and a `pathPrefix` at each scope, no `rooms`; `{"$param": "slug"}` takes an argument, `"as": "integer"` converts it; `boundQuery` from `@agentshouse/mdruntime/entry` fills the same query in your page), `interaction` (the queries its first interaction needs), `title`, `description` and `body`. A body node is text, `{text: v}`, `{markdown: v}` of a document, `{tag, attrs, children}`, `{each: v, as, children}` or `{if: v, then, else}`; a value `v` is a literal, `{param}`, `{at, pointer}` into a query result or `each` item, `{join: [v]}`, `{count: v}` or `{stem: v}`. An `img`, a document's images among them, loads lazily in the box its `width` and `height` name, 16:9 without them. `styles` lists release CSS files. A route that fails starts the App as usual, and the Site document names why.
|
|
5
|
+
Entry Data Contract, `entry_data` in a Site App’s `app.json`: House serves each route as HTML from your Rooms before the App starts, running none of your code. Each of `routes` has a `path` (`/`, `/posts/:slug`), optional `query` (functional `?name` parameters with defaults), `queries` (Local Query objects with `types` and a `pathPrefix` at each scope, no `rooms` or `fileIds`; `{"$param": "slug"}` takes an argument, `"as": "integer"` converts it; `boundQuery` from `@agentshouse/mdruntime/entry` fills the same query in your page), `interaction` (the queries its first interaction needs), `title`, `description` and `body`. A body node is text, `{text: v}`, `{markdown: v}` of a document, `{tag, attrs, children}`, `{each: v, as, children}` or `{if: v, then, else}`; a value `v` is a literal, `{param}`, `{at, pointer}` into a query result or `each` item, `{join: [v]}`, `{count: v}` or `{stem: v}`. An `img`, a document's images among them, loads lazily in the box its `width` and `height` name, 16:9 without them. `styles` lists release CSS files. A route that fails starts the App as usual, and the Site document names why.
|
|
6
6
|
|
|
7
7
|
```json
|
|
8
8
|
{
|
package/guide/sites/runtime.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The App runtime on a Site
|
|
2
2
|
|
|
3
|
-
What the App runtime does differently on a Site's page.
|
|
3
|
+
What the App runtime does differently on a Site's page. Everything else is as in an App, [Local first](apps/local-first) included: House serves a Site's first screen as HTML from its [Entry Data Contract](sites/entry-data-contract) before the App starts, so a Site's page draws no loading state either.
|
|
4
4
|
|
|
5
5
|
A History revision or a Document's `change` written by a Site gives its `author` as `{ kind: 'site', id, name }`. `observe('member')` answers `null` on a Site, and a Document's `change` is `null` there, since the Provider delivers no time.
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ Run `wayfinder` to chart the map; this page adds only what is House's. `wayfinde
|
|
|
16
16
|
|
|
17
17
|
## Keep the plan
|
|
18
18
|
|
|
19
|
-
- Keep the map as one `wayfinding-map` and its tickets as `wayfinding-ticket` documents in My Room, which `list_rooms` marks with `my_room: true
|
|
19
|
+
- Keep the map as one `wayfinding-map` and its tickets as `wayfinding-ticket` documents in My Room, which `list_rooms` marks with `my_room: true`, each declaring `type: wayfinding-map` or `type: wayfinding-ticket` in its frontmatter: a field named for the type makes no map. Chart the map even when the way looks clear: My Room's map is how House knows onboarding ran.
|
|
20
20
|
- Keep decisions about the account, billing, runtime or credentials in the Private Room, `/rooms/private`.
|
|
21
21
|
- Make the App for your User's goal a ticket.
|
|
22
22
|
|
|
@@ -16,13 +16,13 @@ In the Room the User names, or My Room when they name none, which `list_rooms` m
|
|
|
16
16
|
- **Destination:** the App the User asked for, built and published.
|
|
17
17
|
- **Notes:** this map's tickets do the work they name; the App's address and `a_` reference once the prototype is published.
|
|
18
18
|
- **Tickets**, each blocked by the one before:
|
|
19
|
-
1. Prototype, `kind: task`:
|
|
20
|
-
2. Design decision, `kind: grilling`: the User
|
|
19
|
+
1. Prototype, `kind: task`: three design variants in one prototype, published as the App's first release.
|
|
20
|
+
2. Design decision, `kind: grilling`: the variant the User picks, with their edits.
|
|
21
21
|
3. Build, `kind: task`: run [`build-app`](skills/build-app).
|
|
22
22
|
|
|
23
23
|
## 2. Publish the prototype
|
|
24
24
|
|
|
25
|
-
Claim the first ticket. Build
|
|
25
|
+
Claim the first ticket. Build three variants as [Build an App](apps/build) says, each a distinct visual direction (type, colour, layout, tone), not three shades of one. Each draws every main page with real-looking content, and a switcher on the page moves between them. Build only pages, their look and their flow: no tests, no data model, no reviews. Before publishing, look at each variant at phone and desktop width and redo any that looks generic or broken.
|
|
26
26
|
|
|
27
27
|
Publish it as a new App, which creates it:
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ Write the App's address and reference into the map's Notes, resolve the ticket w
|
|
|
33
33
|
|
|
34
34
|
## 3. Record the User's decision
|
|
35
35
|
|
|
36
|
-
Claim the second ticket and ask the User to open the prototype and
|
|
36
|
+
Claim the second ticket and ask the User to open the prototype and pick one variant, naming any edits or pieces to take from the others. Publish the chosen variant with those edits, without the switcher, as the next release of the same App: `npm run publish` without `--name`, or `publish_app_zip` naming the App. Repeat with each round of edits until the User keeps it. Resolve the ticket with their decision in their words; never decide for them.
|
|
37
37
|
|
|
38
38
|
## 4. Build
|
|
39
39
|
|
package/owner-rules-earlier.json
CHANGED
|
@@ -5,5 +5,6 @@
|
|
|
5
5
|
"ac814a2197038f037b657bb719340395f89f319ac9fcdd386629c3fe2cec68f1",
|
|
6
6
|
"29951ee394aad9979eecfed3e5dc32c2c86e2226b6a380db5dbd4f305f4d1156",
|
|
7
7
|
"b99799a2fcddcb99e6ac14acc8d7a92a9f8f69b53f6b41edbc6f8e978256c71a",
|
|
8
|
-
"d14166e300ce35455ca51ec4959c2bc77668e5cb7f890d2b89c4d2c1e02af6b5"
|
|
8
|
+
"d14166e300ce35455ca51ec4959c2bc77668e5cb7f890d2b89c4d2c1e02af6b5",
|
|
9
|
+
"e612bbaa90e41d60b9bcd0367a074b9735b9239c287e56fa1becc8364562a422"
|
|
9
10
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentshouse/skills",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-ca3affce380c3",
|
|
4
4
|
"description": "The upstream Skills agents.house serves its agents, verbatim, and the texts it writes for them: the Guide, its own Skills among the pages, and the Agent instructions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/set/code-review/SKILL.md
CHANGED
|
@@ -8,7 +8,7 @@ Two-axis review of the diff between `HEAD` and a fixed point the user supplies:
|
|
|
8
8
|
- **Standards**: does the code conform to this repo's documented coding standards?
|
|
9
9
|
- **Spec**: does the code faithfully implement the originating issue / spec?
|
|
10
10
|
|
|
11
|
-
Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings.
|
|
11
|
+
Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings. Start every sub-agent with a fresh context window.
|
|
12
12
|
|
|
13
13
|
The issue tracker should have been provided to you. If not, tell the user to run `/setup-matt-pocock-skills`.
|
|
14
14
|
|
|
@@ -18,7 +18,7 @@ Show this to the user, then immediately proceed to Step 2. The user reads and th
|
|
|
18
18
|
|
|
19
19
|
### 2. Spawn sub-agents
|
|
20
20
|
|
|
21
|
-
Spawn 3+ sub-agents in parallel. Each must produce a **radically different** interface for the deepened module.
|
|
21
|
+
Spawn 3+ sub-agents in parallel, each with a fresh context window. Each must produce a **radically different** interface for the deepened module.
|
|
22
22
|
|
|
23
23
|
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
|
|
24
24
|
|
package/set/grilling/SKILL.md
CHANGED
|
@@ -25,6 +25,6 @@ Word each question so "yes" accepts your recommended answer.
|
|
|
25
25
|
|
|
26
26
|
Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one.
|
|
27
27
|
|
|
28
|
-
Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait.
|
|
28
|
+
Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent with a fresh context window to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait.
|
|
29
29
|
|
|
30
30
|
The session is done when the frontier is empty: every branch of the design tree visited, nothing left silently assumed. Do not act on it until the user confirms you have reached a shared understanding.
|
|
@@ -12,6 +12,8 @@ The goal is the entire spec implemented on a single **integration branch**, with
|
|
|
12
12
|
|
|
13
13
|
The tickets are not a list of steps. They are a **task graph** with blocking relationships between them. This means there is always a **frontier** of tickets which are ready to be grabbed.
|
|
14
14
|
|
|
15
|
+
Start every subagent with a fresh context window.
|
|
16
|
+
|
|
15
17
|
Communication to and from subagents should be sparse. Communicate primarily through **context pointers**: to the spec, tickets, research notes, and previous commits. Don't duplicate information already available via pointers.
|
|
16
18
|
|
|
17
19
|
**Implementer subagents** should be run in the background where possible for maximum concurrency.
|
|
@@ -24,7 +26,7 @@ Communication to and from subagents should be sparse. Communicate primarily thro
|
|
|
24
26
|
|
|
25
27
|
3. Create the integration branch. If the issue tracker closes work through PRs, or the user asks for one, open a draft PR after the first merge in step 5 (a branch with no commits ahead of main can't open one), marked as closing the spec and tickets.
|
|
26
28
|
|
|
27
|
-
4. Use **implementer
|
|
29
|
+
4. Use a fresh **implementer subagent** for each ticket, each in its own worktree on its own branch. Each implementer subagent:
|
|
28
30
|
- confirms its worktree is based on the integration branch before starting, and resets onto it if not;
|
|
29
31
|
- calls the Skill tool with `tdd` to build the ticket;
|
|
30
32
|
- merges the integration branch tip into its own branch before reporting done
|
|
@@ -33,7 +35,7 @@ Communication to and from subagents should be sparse. Communicate primarily thro
|
|
|
33
35
|
|
|
34
36
|
6. If this changes the **frontier** of available tickets, kick off more **implementer subagents** to work on the new tickets. This allows for maximum concurrency.
|
|
35
37
|
|
|
36
|
-
7. Once all tickets are complete, call the Skill tool with `code-review` on the integration branch. Fix all issues raised by the code review in a single **implementer subagent**.
|
|
38
|
+
7. Once all tickets are complete, call the Skill tool with `code-review` on the integration branch. Fix all issues raised by the code review in a single fresh **implementer subagent**.
|
|
37
39
|
|
|
38
40
|
8. If a draft PR exists, mark it ready for review. Otherwise, resolve each ticket the way the issue tracker closes work, and report the integration branch.
|
|
39
41
|
|
|
@@ -24,7 +24,7 @@ This command is _informed_ by the project's domain model and built on a shared d
|
|
|
24
24
|
|
|
25
25
|
Read the project's domain glossary (`GLOSSARY.md`) and any ADRs in the area you're touching first.
|
|
26
26
|
|
|
27
|
-
Then spawn a sub-agent to walk the codebase. Don't follow rigid heuristics; explore organically and note where you experience friction:
|
|
27
|
+
Then spawn a sub-agent with a fresh context window to walk the codebase. Don't follow rigid heuristics; explore organically and note where you experience friction:
|
|
28
28
|
|
|
29
29
|
- Where does understanding one concept require bouncing between many small modules?
|
|
30
30
|
- Where are modules **shallow**, with an interface nearly as complex as the implementation?
|
package/set/research/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: research
|
|
|
3
3
|
description: Investigate a question against high-trust primary sources and capture the findings as a Markdown file in the repo. Use when the user wants a topic researched, docs or API facts gathered, or reading legwork delegated to a background agent.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Spin up a **background agent** to do the research, so you keep working while it reads.
|
|
6
|
+
Spin up a **background agent** to do the research, so you keep working while it reads. Start it with a fresh context window.
|
|
7
7
|
|
|
8
8
|
Its job:
|
|
9
9
|
|
package/set/wayfinder/SKILL.md
CHANGED
|
@@ -112,7 +112,7 @@ User invokes with a loose idea.
|
|
|
112
112
|
2. **Map the frontier.** Grill again, **breadth-first** this time: fan out across the whole space rather than deep on any one thread, surfacing the open decisions and the first steps takeable now. **If this surfaces no fog** (the way to the destination is already clear, the whole journey small enough for one session), you don't need a map. Stop and ask the user how they'd like to proceed.
|
|
113
113
|
3. **Create the map** (label `wayfinder:map`): Destination and Notes filled in, Decisions-so-far empty, the fog sketched into **Not yet specified**.
|
|
114
114
|
4. **Create the tickets you can specify now** as child issues of the map, then wire blocking edges in a **second pass** (issues need ids before they can reference each other). Write cross-references in that pass too, with real ids: a placeholder `#<n>` auto-links to an unrelated issue. Wiring sorts them into the frontier and the blocked; everything you can't yet specify stays in the fog: the **Not yet specified** section.
|
|
115
|
-
5. **Fire the research subagents.** For each `research` ticket you just created, spin up a subagent that calls the Skill tool with "research" to resolve it in parallel, capturing its findings on a throwaway `research/<name>` branch with a context pointer from the ticket. Push the branch but open no PR: it is never merged.
|
|
115
|
+
5. **Fire the research subagents.** For each `research` ticket you just created, spin up a subagent with a fresh context window that calls the Skill tool with "research" to resolve it in parallel, capturing its findings on a throwaway `research/<name>` branch with a context pointer from the ticket. Push the branch but open no PR: it is never merged.
|
|
116
116
|
6. Stop: charting is one session's work; it hand-resolves nothing.
|
|
117
117
|
|
|
118
118
|
### Work through the map
|
package/upstream.json
CHANGED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Local Search rules
|
|
2
|
-
|
|
3
|
-
How `search` finds Documents and messages by words on the device.
|
|
4
|
-
|
|
5
|
-
`search({ text, documents, collection, conversations, limit, after, total })` answers
|
|
6
|
-
once, by words, from the full-text index the browser keeps with its local state, so it
|
|
7
|
-
answers at once after a reload. There is no observed search.
|
|
8
|
-
|
|
9
|
-
- **Declaration.** Only an App whose release declares Local Search searches; any other
|
|
10
|
-
App's `search` answers the never retryable `undeclared` refusal `search-undeclared`, its
|
|
11
|
-
`fields` naming `local_search`.
|
|
12
|
-
- **What is found.** A Document by its field values, its sections' headings and text, its
|
|
13
|
-
preamble and its Path, and a message by its text, never a frontmatter key, a Room Draft,
|
|
14
|
-
a chat draft or a running turn's parts. Any word found ranks a hit, more found rank it
|
|
15
|
-
higher, and adjacent words higher still; case and the marks of Latin, Greek and
|
|
16
|
-
Cyrillic letters fold, English word forms stem, the last word also matches as the start
|
|
17
|
-
of a word unless the text ends with a space, and Chinese, Japanese, Korean and Thai text
|
|
18
|
-
matches by character pairs. There is no typo tolerance and no operator syntax.
|
|
19
|
-
- **Narrowing.** `documents` is a Local Query filter, and `collection: true` leaves out
|
|
20
|
-
what each Room's `typedSelection` roots hold; `conversations` names conversations. All
|
|
21
|
-
apply before `limit`, which bounds each list, at most 1,000 whatever a search names. A
|
|
22
|
-
complete answer it cuts short names `next`, which `after` takes for the next window of
|
|
23
|
-
both lists. `total: true` adds `total`, each list's count. A word in very many Documents
|
|
24
|
-
ranks among a bounded set of them, the Documents of a rarer word of the search always
|
|
25
|
-
among them.
|
|
26
|
-
- **Results.** `{ documents, messages, coverage }`, or the same with `pending` naming each
|
|
27
|
-
Room the search still waits for: show its hits, ask again, and never read pending as no
|
|
28
|
-
hits. `coverage` names the Rooms and conversations searched. Each list is ranked by
|
|
29
|
-
`score`, comparable only within it; ties come by `room`, then `path`, or by
|
|
30
|
-
`conversation`, then `identity`. A Document hit names its `room`, `path` and `type`, a
|
|
31
|
-
message hit its `conversation` and `identity`; `matches` maps each word of the search
|
|
32
|
-
to the parts it was found in: `fields`, `sections`, `preamble`, `path` or `text`. A hit
|
|
33
|
-
whose words were found carries `snippet: { part, text, at }`: the part's text around
|
|
34
|
-
the first word found, at most 80 characters on either side, and `at` that word's start
|
|
35
|
-
and end in `text`. Mark the words you show. A bad filter fails as a query does.
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Routine
|
|
2
|
-
|
|
3
|
-
How a Routine document in the Private Room is written, one for each Room it serves.
|
|
4
|
-
|
|
5
|
-
Routine, `/rooms/private/rooms/<room-handle>/routines/<name>.md`, serving the Room `<room-handle>` names: `description`; `input`, a text saying what to send, empty if nothing; and `agent`, the identifier of the one of your User's current Agents it starts. The body is the instruction, which never leaves the Private Room. Writing it needs your User's write in that Room. House writes its card, the description, input and Agent, at `/rooms/<room-handle>/house/routines/<name>.md`, where every member reads it, and refuses a name a card there already holds as `routine_name_taken`. A Routine a second Room needs is a copy under that Room's handle. Anyone who writes that Room asks it with `send_routine_request` naming the card, or names the `agent` with their own words, as the Agent's Trust level in that Room allows: under Any request, the default, either starts at once; under Routines only the card starts at once and own words are refused `own_text_not_admitted`; under Approve each either waits for the Agent's owner to approve it. Its owner sets the level on the Agent's House page; the owner, their own agents and their Private Room's Triggers start it at once at every level, a Trigger of any other Room asks as that Room's writers do, and `inspect` on a Room root names the Agents you may ask there with their levels. A Routine Request's input is text the Agent reads, and House checks no shape. Mail your conversation sends with `send_mail` and a request it sent reaches the conversation that request started at once under Any request; below it, `send_mail` answers `waiting: true` and the mail waits for that Agent's owner, and a declined one comes back to you as House's message saying so. That conversation's mail always comes back to yours. A request's run is the first turn of the conversation it starts: during that turn the Agent names the document it wrote in the request's Room as the request's result with `set_request_result`, and the turn's end finishes the request. The conversation stays active until it is Done, which `mark_done` sets at the end of a turn and which nothing undoes, so mail reaches it after its run.
|
|
6
|
-
|
|
7
|
-
A Trigger's `message` action puts its `text`, a link to what matched and its files into the active conversation, one not Done, its Routine started from that Room for what the Event is about: the chat of a customer's `inbox-message`, the matched document or item, or the Trigger for a `cron` clock. Its Event shows `continued` with that `request`; with none, it starts the Routine as a `routine` action does, `requested`. A conversation waiting for approval refuses it, `refused` with `conversation_waiting`. The Agent reads a customer's message as a customer's words, one from a Trigger of its owner's Private Room as the owner's instruction, and any other as what matched, never an instruction. A `routine` action starts a new request on every Event.
|
|
8
|
-
|
|
9
|
-
```markdown
|
|
10
|
-
---
|
|
11
|
-
description: Summarize a thread in five bullets.
|
|
12
|
-
input: The thread to summarize, as a ref or pasted text.
|
|
13
|
-
agent: 019a0000-0000-7000-8000-000000000001
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
Read the thread and write five bullets under summaries/.
|
|
17
|
-
```
|