@passioncode-ai/fabric-agent-adapter 0.57.0 → 0.59.0

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.
Files changed (21) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/COMMERCIAL-LICENSE.md +1 -1
  3. package/README.md +17 -6
  4. package/SECURITY.md +6 -1
  5. package/package.json +1 -1
  6. package/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  7. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +18 -3
  8. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/operator-channel-and-repository.md +4 -1
  9. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/references/source-and-instance.md +179 -0
  10. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py +995 -0
  11. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_homes.py +72 -0
  12. package/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/fabric_source_ignore.py +185 -0
  13. package/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +15 -17
  14. package/plugins/fabric-agent-adapter/skills/building-fabric-services/references/package.md +22 -0
  15. package/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +181 -1
  16. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +29 -8
  17. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/operator-channel-and-repository.md +4 -1
  18. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/source-and-instance.md +179 -0
  19. package/plugins/fabric-agent-adapter/skills/creating-fabric-agents/references/where-agents-run.md +23 -1
  20. package/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
  21. package/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
@@ -5,7 +5,7 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
5
5
  compatibility: Requires filesystem access and Python 3.9+. Exact schema checks additionally need git, Node.js, pnpm, and the pinned fabric-agent-contract checkout (a public repository). Works without those tools in an explicitly degraded structural-check mode. Ships in one plugin with adapting-projects-to-fabric, whose scripts it reuses.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.57.0"
8
+ version: "0.59.0"
9
9
  contract-version: "0.1.0"
10
10
  contract-commit: "3830a3353923e20f826d466610e1c479f120bec0"
11
11
  ---
@@ -57,6 +57,9 @@ No file is created until every row has an answer. An answer of "later" fails the
57
57
  | **Which profile?** | `mcp` when Fabric owns planning and retries and the capability has a schema; `a2a` when the provider owns its task lifecycle end to end; `local-runner` when it is a terminal agent Fabric drives. When unsure, the sibling skill's profile-selection reference is the decision record |
58
58
  | **What effects?** | declare anything money-, deletion-, or publication-adjacent now; the host's floor and grants are designed against these declarations, and one discovered late invalidates the admission |
59
59
  | **Where does it run, for whom?** | this machine, the person's cloud or an organization's, and in which contexts (`personal`, `org:<id>`): one instance per context, its secrets, spend and journal never crossing into another. Record which capabilities need the person's machine and what they answer when it is off (references/where-agents-run.md) |
60
+ | **Does it run around the clock?** | the uptime category, decided now: **A** when a capability must answer while the owner's machine is off or asleep (a schedule, a chat, callers outside the machine): the agent gets a cloud host from its first release, Heroku unless a reason names another (the person's or an organization's cloud, or PassionCode's for a paid agent), built as an online service (building-fabric-services, the remote placement) and started from the cloud Fabric-server template once that template's repository exists; **B** when nothing needs it then: a local service only, no cloud, reading an A agent's data through that agent's MCP or API. The README names the category (references/where-agents-run.md) |
61
+ | **Whose data does it keep?** | the kind of its data, named in the README: **U** when it keeps data of people outside the owner's team (messages, support requests, reviews, customers' answers), which never enters the source, a copy of it or its tests: the source holds the schema and synthetic fixtures, and a copy starts with an empty store of its own; **O** when it keeps none (a tool or service for other agents, the owner's reports, runs and media). In doubt, U. Either way the data lives in `<agent>-data` (a kit service: the kit's folders; on a cloud host: a durable store), apart from the source, from the first commit (references/source-and-instance.md) |
62
+ | **Who holds the copyright?** | the holder the agent's `LICENSE` names: a new private agent (built for its owner or for a client) gets a `LICENSE` naming that holder in its first commit, all rights reserved unless the owner gives other terms; a public product keeps its own licence (a PassionCode.ai repository: the organization's, step 2) |
60
63
  | **Which models?** | per lane (planning, replies, volume work, media): a primary, a fallback of another vendor, an open-vendor model where it reads what users send, the input kinds each reads, declared as classes in the profile (references/models-and-providers.md) |
61
64
  | **Who pays?** | one key per agent and context with its own ceiling, from the estate's key manager, read from the environment; checked before every call; a personal key never pays for an organization |
62
65
  | **How does it learn?** | decisions recorded with reasons, one trace per turn, every failure a planted fixture first, history replayed, learnings proposed and never self-approved (references/orchestration-and-learning.md) |
@@ -73,8 +76,10 @@ failures, a retro), distil it before writing code:
73
76
  failures and dead ends).
74
77
  3. **Convert every trap into a planted eval fixture.** The new agent is not done until
75
78
  it has been watched rejecting the exact defects its predecessors were burned by:
76
- knowledge transfers as a check, not as prose.
77
- 4. Record the pack in the new project as `docs/knowledge-pack.md` with its sources, so
79
+ knowledge transfers as a check, not as prose. A fixture carries the defect on neutral
80
+ ids, never the predecessor's owner, paths or accounts.
81
+ 4. Record the pack in the new project as `docs/knowledge-pack.md` with its sources (a public URL or commit, else a
82
+ neutral name, the private address kept in the instance), so
78
83
  the provenance of every borrowed decision survives the person who borrowed it.
79
84
 
80
85
  Skipping this step because there is no prior art is legal; skipping it because reading
@@ -109,8 +114,11 @@ are its seed because a source project's recorded failures count as production.
109
114
  repository standard (`knowledge/repository-standard.md`) from its first commit: the licence files,
110
115
  the README quick start verified with a real client, `AGENTS.md` and `CLAUDE.md`, as
111
116
  [the operator channel and the repository standard](references/operator-channel-and-repository.md)
112
- lists them. An agent someone builds for themselves is not a PassionCode.ai repository: its licence
113
- is its owner's choice and none of those files is required of it.
117
+ lists them. An agent someone builds for themselves, or a private agent built for a client, is not a PassionCode.ai repository: its licence
118
+ is its owner's choice and none of those files is required of it, but its first commit carries a `LICENSE` naming
119
+ the copyright holder the grill recorded (all rights reserved unless the owner gives other terms); it covers the
120
+ holder's own files, and the kit files the adapter copies in keep their own licence (AGPL-3.0-only OR
121
+ LicenseRef-PassionCode-Commercial).
114
122
 
115
123
  **Every credential the agent uses is read by name, never held as a value**, from its first commit:
116
124
  the README's Configure section lists key NAMES. With Project Observatory installed, the agent finds
@@ -122,6 +130,15 @@ at start. Either way: no `.env` of its own, no key file beside its data, no valu
122
130
  configuration or a commit. A workflow names the keys it needs in its checkpoint (`credentials`),
123
131
  never their values.
124
132
 
133
+ **The source stays apart from the instance**, from the first commit: instance values (the
134
+ repository address of the owner's account, folders, the owner, accounts, hosts) live in a configuration outside the repository, read through a
135
+ named variable with a neutral default and a committed `*.example`; records, logs and state documents live in the
136
+ instance folder; fixtures use neutral ids. Its data has a home of its own from the same commit, the one the grill recorded: `<agent>-data` (a folder beside the
137
+ checkout, or a private repository of that name; a kit service: the kit's folders; on a cloud host: a durable store), and the README names the data kind (U or O) and the uptime
138
+ category (A or B) from the grill. Load [the source and the instance](references/source-and-instance.md). The
139
+ sibling's `scripts/fabric_source_check.py check <project-root>` finds instance data in the tree without repeating it;
140
+ run it with the owner's list of their own strings, or record that none was given (the owner's own home name, when it is neutral, has one or two letters, or holds a space, `.`, `_`, `-` or `'`, is declared there with `home: <name>` first; a container's, a cloud image's or a CI runner's user that only the image uses, such as `node` or `vscode`, is never declared); the skeleton is done when it exits 0 and the owner has reviewed it (a floor, not a secret scanner; without the sibling or Python 3.9+, record the check as NOT_RUN, as in step 3). Handing the source over stays the owner's act.
141
+
125
142
  ## Step 3: scaffold the provider bundle
126
143
 
127
144
  Reuse the sibling skill's scaffolder against the new skeleton with the profile chosen at
@@ -163,7 +180,8 @@ reference). An agent that is not a service is announced with a provider entry
163
180
  If the agent keeps running on the operator's computer (it answers other agents, runs
164
181
  long jobs or shows a dashboard), build it as a service with the sibling skill
165
182
  `building-fabric-services`: its instance lock, launchd plist, descriptor, well-known
166
- document and events feed are part of this step, not a later retrofit. If that skill is
183
+ document and events feed are part of this step, not a later retrofit. An uptime A agent is built with the same
184
+ skill as an online service (its remote placement: the https origin, keys as the platform's secrets). If that skill is
167
185
  absent, apply the `fabric-service/0.1` rules from the pinned contract's
168
186
  `docs/specification/service.md` by hand and mark its probe `NOT_RUN`.
169
187
 
@@ -181,8 +199,11 @@ lets the output decide what counts as success.
181
199
 
182
200
  Run the sibling skill's `check` (structural, then exact when the contract checkout is
183
201
  available) and complete `fabric/FABRIC-CONFORMANCE.md` with dated receipts, exactly as
184
- its workflow steps 5–7 prescribe; this skill adds no second verification path on
185
- purpose. Then record one expectation the report must carry:
202
+ its workflow steps 5–7 prescribe, and run its `fabric_source_check.py check` again (exit 0). Every release of the
203
+ agent runs it too, in Git mode on the checkout and with `--every-file` on the clean copy a transfer would carry (what
204
+ Git would commit, as building-fabric-services' `references/package.md` makes it), as the transfer audit (each exit 0, with the owner's list or
205
+ its absence recorded) until the contract's export (CT-194) replaces it; this skill adds no second verification path
206
+ on purpose. Then record one expectation the report must carry:
186
207
 
187
208
  > When a Fabric host admits this provider, it enters under a **canary binding** (a
188
209
  > checker on its output and a budget cap), regardless of who wrote it. Unsupervised
@@ -63,4 +63,7 @@ the project).
63
63
  org-index `scripts/check_format.py` reports 0 findings for it before it is called done. An agent
64
64
  someone builds for themselves is not a PassionCode.ai repository: its licence is its owner's
65
65
  choice, nothing about it is published or listed by the organization, and none of these files is
66
- required of it, though the verified MCP quick start is still how anyone learns to drive it.
66
+ required of it, except that an agent built new carries a `LICENSE` naming the copyright holder (all rights reserved
67
+ unless the owner gives other terms), as does a new private agent built for a client; files the adapter copies in (the service kit, the dashboard kit) keep their own
68
+ licence, `AGPL-3.0-only OR LicenseRef-PassionCode-Commercial` ([COMMERCIAL-LICENSE.md](https://github.com/passioncode-ai/fabric-agent-adapter/blob/main/COMMERCIAL-LICENSE.md)).
69
+ The verified MCP quick start is still how anyone learns to drive it.
@@ -0,0 +1,179 @@
1
+ # The source and the instance
2
+
3
+ An agent is two things. Its **source** is what can be handed to someone else: code, skills, prompts, schemas,
4
+ examples, the source documentation. Its **instance** is what belongs to one installation: records, logs, journals,
5
+ keys, the owner's names and paths, accounts, hosts and its configuration. A new agent keeps the two apart from its
6
+ first commit, so that its owner can hand the source over later without rewriting its history. Nothing is handed over
7
+ by itself: handing over, a new remote, a push or a change of visibility stays the owner's act, each time (a personal
8
+ agent stays private until its owner says so about that act).
9
+
10
+ The format of a source manifest, export and import is the contract's to decide (CT-194). What follows does not depend
11
+ on it.
12
+
13
+ ## Contents
14
+
15
+ - [The layout](#the-layout)
16
+ - [The data home and its kind](#the-data-home-and-its-kind)
17
+ - [The check](#the-check)
18
+
19
+ ## The layout
20
+
21
+ | Where | What | In Git |
22
+ |---|---|---|
23
+ | the repository | code, skills, prompts, schemas, the source documentation, fixtures on neutral ids (`example-agent`, `/Users/example`) | yes |
24
+ | `config/<name>.example.json` (or `.env.example`, `*.example.yaml`) | every instance value the agent reads, with a neutral default: the repository address of the owner's account, folders, the owner, account ids, hosts | yes, the example only |
25
+ | the instance folder, outside the repository | the real configuration, records (a database included), logs, journals, state documents | never in the source's repository (a separate private repository of the owner's data belongs to the instance) |
26
+ | the secret store | key values; the source names each key by name only (`EXAMPLE_API_KEY`) | never |
27
+
28
+ - **The instance folder is the agent's data folder.** An agent that is not a kit service reads it through one named
29
+ variable (`<AGENT>_DATA_DIR`) that overrides its default. A service built with `building-fabric-services` takes the
30
+ kit's folders unchanged (the Directories table in its `references/lifecycle.md`, `fabric_paths.service_dirs`): on macOS `~/Library/Application Support/<id>/`,
31
+ on Linux `${XDG_DATA_HOME:-~/.local/share}/<id>/`, on Windows `%LOCALAPPDATA%\<id>\`. An agent's second context
32
+ (an organization's) is a sibling instance, `<id>.<instance>`, never a folder inside the personal one
33
+ (`references/estate-record.md` of that skill).
34
+ - **Ignore the instance in the source**: `.gitignore` names the real configuration (`config/*.json` with
35
+ `!config/*.example.json`), an instance folder an existing project still keeps inside the checkout, and what running
36
+ the agent leaves (`__pycache__/`, `*.pyc`), which the check would otherwise report as binary in Git mode.
37
+ - **State documents are instance data.** A handoff, a decision log or a backlog about this codebase is source
38
+ documentation; a record of what this installation did (runs, conversations, the owner's notes, a journal) lives in
39
+ the instance folder.
40
+ - **Keys by name.** The README's Configure section lists key names, the agent reads each from its environment or the
41
+ secret store, and no value is ever written to a file, a fixture, an argument or a commit.
42
+ - **Fixtures on neutral ids.** A test that needs a home folder, an owner or an account uses `example`, `example-agent`,
43
+ `owner@example.com` or a one-letter home, and a credential-shaped test value says it is synthetic
44
+ (`sk-test-not-a-key`) or is assembled at run time.
45
+ - **Two names stay in the source until the contract decides (CT-194).** A service's supervisor label is the agent's
46
+ own name, reverse-DNS on the publisher's domain (`com.example.example-agent`), and `AGENTS.md` names it (LC-09):
47
+ never the owner's account or name. A provider's `createdBy` names the publisher; until one is chosen it
48
+ keeps the scaffold's sentinel, and a person's name or account is never written there. The adapter moves both when the
49
+ contract decides how an instance renames them (FAA-188 part 2).
50
+
51
+ ## The data home and its kind
52
+
53
+ - **The data has a home of its own from the first commit: `<agent>-data`.** For an agent that is not a kit service it
54
+ is a folder beside the checkout, never inside it, and `<AGENT>_DATA_DIR` names it (its default is
55
+ `../<agent>-data`); a kit service keeps the kit's folders above. When the owner keeps the data in Git, it goes to a
56
+ separate private repository named `<agent>-data`, never to the source's repository. On a cloud host that does not
57
+ keep its disk across a restart (Heroku and the like, an uptime A agent's default), the data home is a durable store
58
+ the same variable names (a database, an object store, that private repository); the folder and the kit's folders
59
+ serve a machine whose disk persists. A copy of the agent starts with an empty home of its own and never connects
60
+ to the original's.
61
+ - **The README names the kind of the data.** **U**: the agent keeps data of people outside the owner's team
62
+ (messages, support requests, reviews, customers' answers); it never enters the source, a copy or its tests, and the
63
+ source holds only the schema and synthetic fixtures (its real-trace corpus and replay samples live in the data
64
+ home). **O**: it keeps none (a tool or service for other agents, the
65
+ owner's reports, runs and media); its working data still goes to the data home. When in doubt, the kind is U.
66
+ - **Every release runs the transfer audit.** `fabric_source_check.py check <project-root>` in Git mode, and
67
+ `check <clean-copy> --every-file` on a clean copy of what Git would commit (`git ls-files --cached --others
68
+ --exclude-standard`, as building-fabric-services' `references/package.md` makes it; never the checkout itself, whose ignored
69
+ configuration and dependency folders `--every-file` reads too), each exit 0, with the owner's list or its absence recorded, until the contract's export (CT-194)
70
+ replaces it.
71
+
72
+ ## The check
73
+
74
+ `fabric_source_check.py check` reads the files the source would carry (what Git would commit: tracked files and
75
+ untracked ones that are not ignored, as they are in the work tree; a staged copy that differs is not read) and reports each place where the instance shows through, by path, line and kind:
76
+
77
+ | Kind | Finds |
78
+ |---|---|
79
+ | `home-path` | an absolute path inside a person's home folder (a web route under `/home/` is reported too; a route under `/users/` is not): `/Users/<name>/`, `/home/<name>/`, `C:\Users\<name>\`, Git Bash's `/c/Users/<name>/`, WSL's `/mnt/c/Users/<name>/`, and the dashed (`-Users-<name>-`, `C--Users-<name>-`) and URL-encoded forms a tool makes of one. The name is ASCII, and in the dashed form it ends at the next dash: an owner whose home name holds a space, a dash, a dot, an underscore or an apostrophe (each a dash in the dashed form; a slashed name also ends at a space) declares it with `home: <name>` in the owner's list, which reads it whole in every form, and one whose home name is not ASCII, or holds another character, lists it as a plain string (a typographic apostrophe a keyboard typed, which the folder's name does not hold, is written as `'` in `home:`; a folder whose name holds `’` is listed as a plain string with `’` kept and declared too with `home: <name>` written with `'`, which reads the dashed forms; its URL-encoded `’` is not read; a name outside ASCII of one or two characters cannot be checked) |
80
+ | `secret` | a value shaped like a credential by the contract's detector (DEC-0044), read line by line: a provider key starting a word, a JWT, a bearer token, a long random value assigned to a credential's name (but a whole Ed25519 signature under a JSON `"sig"`, `"signature"` or `"signatures"` member, as `sign` writes it, which is public); or a private key: a PEM, OpenSSH or PGP block, or a JWK's `d` as a JSON member, escaped JSON or a Python dict; a URL with a password of 8 or more characters mixing letters and digits (a password without a digit or without a letter is not read) or a credential in its query, percent-encoded or not, JSON-escaped (`\u0026`, `\/`) or nested in another URL's parameter (two levels deep); a file that is a credential by its name, as `pack` refuses it (`.env`, `*.pem`, `*.key`, `.npmrc` and the rest), or a kit credential file (`*.token`, `board.credential`) |
81
+ | `instance-string` | a string from the owner's list, in a file, in a file's path or in a link's target |
82
+
83
+ ```bash
84
+ python3 <plugin-dir>/skills/adapting-projects-to-fabric/scripts/fabric_source_check.py check <project-root>
85
+ ```
86
+
87
+ - It **never repeats** a value it found: no value, no line holding one, no entry of the owner's list and not the
88
+ folder it checks reaches its output. A file's path is checked like its text (line `0` of a finding), and printed
89
+ with an entry of the list as `<line N>`, a home folder's name as `<home>` and each segment holding a
90
+ credential-shaped value as `<secret>` (two files can then print the same; the segments a found value runs across, an
91
+ assigned value, a bearer run or a URL, are masked too, while a key, a JWT or a JWK `d` is read up to the `/` that
92
+ cuts it, so the rest of a cut one can print in the next folder: find the file by the folder the finding names); an instance string is named by its line in the owner's list. The report can go in a commit
93
+ message, an issue or a chat. A declared home of one or two letters, once found anywhere in the tree, is masked wherever it stands in a printed
94
+ path, inside a word too (`qzone` prints `<home>one`); found nowhere, it is not masked.
95
+ - It **changes nothing.** Exit `0`: no finding and every file read; `1`: at least one finding (the step is not done);
96
+ `2`: the check could not run, or a file was not read: an unreadable file, a nested repository, or a binary file the
97
+ ignore file does not name as source (the summary counts each, and how many files were left out by name). A finding
98
+ sets `1` even when a file was not read too; the files not read are still listed, so fixing the findings can leave `2`.
99
+ - **What it reads.** A text file of any size is read whole, and the summary counts every text file read; a binary file (a NUL byte in its first 8 KiB) or one that
100
+ cannot be read is listed by its path as not read, and the summary counts it. A database or another binary of
101
+ records in the tree is instance data the check cannot read: move it to the instance folder. A binary that is source
102
+ (an icon, a font) is named in the ignore file. `--every-file` reads every file, those Git ignores included, but `.git`, `__pycache__`, `.DS_Store` and `*.pyc`
103
+ (litter `pack` refuses): run it
104
+ on a folder before `pack`, which carries them. A committed symbolic link is checked by the path it names, never followed. When the folder is
105
+ not in a Git checkout, Git lists nothing in it, or no `git` is found in PATH's absolute folders (with PATH unset, in `/bin` and `/usr/bin`; on Windows, nowhere), every file is read but `.git` and `__pycache__` (uncounted) and the dependency
106
+ folders (`node_modules`, virtual environments, caches), and the summary says Git was not used and how many dependency folders were not read.
107
+ Under a locale whose file-system encoding is not UTF-8 (ISO-8859-1, KOI8-R) the check restarts itself in Python's UTF-8
108
+ mode, so a file name, an argument and a file's text are read in one encoding; a name that is not UTF-8 is matched as its bytes.
109
+ A file's text is read as UTF-8, so an entry of the owner's list with a letter outside ASCII is found in a UTF-8 file
110
+ (composed or decomposed, as macOS may store a name), not in one saved as Latin-1 or CP1252.
111
+ In Git mode a nested repository (a submodule, a vendored checkout) is not read: check a nested repository in its own tree with
112
+ the same check (a submodule checked out first, `git submodule update --init`), then name its path in the ignore file,
113
+ with that reason on the line above. That line serves the check of the outer tree only: before `pack`, check the
114
+ nested repository with `--every-file` in its own tree too, because `pack` carries its files, ignored ones included.
115
+ - **The owner's list** holds the strings only the owner knows are theirs (their name, company, accounts, hosts), one
116
+ per line, at least three characters; a line starting with `#` is a comment, so list a channel or a hashtag without its `#`, and a `#` after a string is
117
+ part of that string. An entry matches in any case and inside any word; an invisible format character (a direction mark, a
118
+ soft hyphen, a joiner) at either end of a string is dropped, spaces between such characters too, and does not count
119
+ toward the three; one inside it is kept and counts (an emoji's joiner), so a string copied with a soft hyphen in it
120
+ matches only text that holds one. A line `home: <name>` declares the owner's home
121
+ name instead (ASCII letters, digits, `.`, `_`, `-`, `'` and spaces, starting with a letter or a digit; a name holding a
122
+ space, as a Windows profile may, is written whole, `home: Jane Doe`, and everything after the colon is the name, so
123
+ a note goes on a line of its own (`# laptop`); spaces and tabs may stand around the colon; a line
124
+ that starts with `home` and a colon and holds anything else is refused, exit 2 (an invisible format character before
125
+ the name, after it or inside `home` is dropped, one inside the name refuses the line; a NUL anywhere in the list
126
+ refuses it too); such a line is never a string
127
+ entry, so list a string like `home: page` by a part of it, `ome: page`): the check then finds that home in every form the table names (it is never neutral, even with one or two
128
+ letters), and the bare name is not looked for anywhere else, though a declared name of three or more letters and digits is
129
+ masked wherever a printed path holds it as a word, its parts joined by none or any run of a space, `.`, `_`, `-`, `+`, `'`, their
130
+ percent-encodings or an escaped apostrophe (`\'`, `\x27`, `\u0027`, `&#39;`, `&#x27;`, `&apos;`), the same separators it is found with in every form; a home that is also your name goes in the list
131
+ as a plain string too, to be found in text. It is `--instance-strings FILE`, else the file
132
+ `FABRIC_INSTANCE_STRINGS` names, else `passioncode-fabric/instance-strings.txt` in the user's configuration folder
133
+ (`$XDG_CONFIG_HOME` when it is absolute, else `~/.config`; `%APPDATA%` on Windows). A list inside the tree is refused, because it would
134
+ travel with the source. A person who already keeps private terms for org-index's `check_private.py`
135
+ (`~/.config/passioncode/private-terms.txt`) passes that file with `--instance-strings`; this check matches a term
136
+ anywhere in a word, and it does not look for e-mail addresses or account ids by their shape.
137
+ - **Leaving paths out.** `.fabric-source-ignore` at the root (globs, one per line, `#` comments) or `--exclude GLOB`
138
+ leaves out files that hold synthetic values on purpose, such as test vectors. A line is a path from the root, not a
139
+ `.gitignore` line (a leading `/` changes nothing, a line with no `/` matches at the root only, and a line holding `\`, `//`, an unclosed `[` or a class such as `[[:digit:]]` is refused, as Git reads each
140
+ otherwise): `*`, `?` and `[...]` stay inside one folder (`?` and `[...]` match one byte, as Git's do, so a letter
141
+ outside ASCII takes two or more) and `**` as a whole segment crosses
142
+ folders, so `vectors/*` takes the files in `vectors` and none in its subfolders. A line ending in `/`, or a path
143
+ with no wildcard, takes that folder and all it holds; only such a line leaves out a nested repository or a
144
+ submodule (`vendor/sub/`), never a wildcard line without the `/` (`vendor/*`). A wildcard folder line (`vendor/*/`)
145
+ takes every nested repository it reaches, checked or not, so name each one by its path. In Git mode a nested
146
+ repository is not read; `--every-file` and a folder outside Git read its files, its `.git` left out. An exception line (`!`) is refused, since what it would
147
+ bring back would go unread. Name the files, or a folder that holds
148
+ only synthetic vectors, never a whole source folder (a nested repository checked in its own tree is the one
149
+ exception), and say why on the line above. The `LICENSE` is the other: its copyright line names the holder, which is
150
+ authorship and travels with the source, so when the holder is on the owner's list the licence file is named in the ignore
151
+ file by its own path (`LICENSE`, `LICENSE.md`) with that reason (`# the copyright holder is authorship and travels with the source`); no other file of the
152
+ owner's is. A file `pack` refuses by its name cannot ship in a package: a public
153
+ certificate is renamed `*.crt`; a project `.npmrc` that holds no credential may be named in the ignore file, with
154
+ that reason, for the source check (the line then hides whatever is added to it later, so keep it credential-free),
155
+ and `pack` still refuses it, so a package is made from a copy without it (its
156
+ settings then apply only where the owner sets them: `~/.npmrc` reaches every project of the owner, and
157
+ `package.json`'s `publishConfig` carries publish-time settings such as `registry`, `tag` and `access`). A file
158
+ holding a credential moves to the instance, never into the ignore file.
159
+ - A neutral home (`example`, `user`, `runner`, a container's, a cloud machine's or a dev container's user such as `node`, `ubuntu` or `vscode`, the fixture id
160
+ `example-agent`, a
161
+ one-letter name, a placeholder; the whole set is `NEUTRAL_HOMES` in the script. An owner whose home name is in it, a
162
+ first name such as `alice` or an `admin` account, or has one or two letters, writes `home: <name>` in the owner's
163
+ list; a container's, a cloud image's or a CI runner's user that only the image uses, such as `node` or `vscode`, is never declared, since
164
+ its paths are the image's and stay in the source; the home of the owner's own machine is declared even when its
165
+ name is an image's user, as `admin` or `ubuntu` on a cloud machine the owner works on; when that name is also the
166
+ user of an image in the tree, the image's paths are reported, and they are written through a build argument that
167
+ names the user, `ARG APP_USER=ubuntu`, `USER ${APP_USER}`, `WORKDIR /home/${APP_USER}/app`, so no home path stands
168
+ in the source; `$HOME` serves only where a shell in the image expands it, a `RUN` line or a script, since a
169
+ Dockerfile's `WORKDIR` and `COPY` see only `ENV` and `ARG` values) and a value that says it is synthetic (`EXAMPLE`, `not-a-key`, `fake`, `dummy`) are
170
+ not findings.
171
+
172
+ The check finds the forms the table names, and only those: a credential in another shape (a bare token with no name
173
+ before it, a provider's key the contract's names do not cover) passes it. It is a floor under the owner's review and a
174
+ secret scanner, never a replacement for either.
175
+
176
+ The check finds the repository from the folder it checks: a Git location the caller exports (`GIT_DIR`,
177
+ `GIT_INDEX_FILE`, as a hook in a linked worktree has) is not passed to Git. The check reads the tree, not Git history. A value that was committed once is still in the history after the tree is
178
+ clean: handing such a source over needs a fresh history, which is the owner's act, never the agent's (no history
179
+ rewrite, no force-push).
@@ -16,6 +16,7 @@ The normative rules are the Fabric Agent Contract's, at the commit this plugin p
16
16
  - [Connecting a machine or a cloud to the estate](#connecting-a-machine-or-a-cloud-to-the-estate)
17
17
  - [Sharing an agent into an organization](#sharing-an-agent-into-an-organization)
18
18
  - [Running while the laptop is closed](#running-while-the-laptop-is-closed)
19
+ - [Uptime: A or B](#uptime-a-or-b)
19
20
  - [Selling an agent](#selling-an-agent)
20
21
  - [The checklist](#the-checklist)
21
22
 
@@ -85,13 +86,33 @@ checked, and removes the instance from the next revision.
85
86
  ## Running while the laptop is closed
86
87
 
87
88
  A cloud agent keeps working with the laptop closed only if everything it needs is in the cloud: its worker, its
88
- secrets, its data, and a hub that can admit calls. The local Fabric hub is not a cloud hub. Design for it:
89
+ secrets, its data, and, for the calls Fabric admits, a hub that can admit them. The local Fabric hub is not a cloud hub. Design for it:
89
90
 
90
91
  - Declare what each capability needs and where it is; a capability that needs the person's machine reports
91
92
  `pending` or `unavailable` instead of pretending, and queues within a deadline or declines.
92
93
  - Never charge for work it cannot deliver on the promised terms.
93
94
  - A test that passed on fixtures says nothing about uptime; measure the installed agent in its cloud.
94
95
 
96
+ ## Uptime: A or B
97
+
98
+ Every new agent is given an uptime category on its first day, and the README names it:
99
+
100
+ - **A, around the clock.** A capability must answer while the owner's machine is off or asleep: a schedule, a chat,
101
+ callers outside the machine. A cloud host is required from the first release; Heroku is the default, another host
102
+ only when a reason names it: an instance in the person's PassionCode cloud or an organization's cloud runs there
103
+ (the places above), and a paid agent stays in PassionCode's cloud (selling an agent, below). On a host with no
104
+ Fabric hub it answers what reaches its own origin (its schedule, its chat, its API); a call Fabric admits needs a
105
+ cloud hub, as running while the laptop is closed says. The agent is an online service (building-fabric-services, the remote placement), often
106
+ alongside its local instance, and starts from the cloud Fabric-server template once that template's repository
107
+ exists. Its data lives where the cloud reaches it, in a durable store (a database, an object store, the private
108
+ `<agent>-data` repository), never the host's own folder, which a platform such as Heroku does not keep across a
109
+ restart.
110
+ - **B, local only.** Nothing needs it while the machine sleeps: a local service and no cloud. It reads an A agent's
111
+ data through that agent's MCP or API, never from its store.
112
+
113
+ A B agent that gains a capability that must answer around the clock becomes A for that capability; record the change
114
+ and move only that capability to the cloud.
115
+
95
116
  ## Selling an agent
96
117
 
97
118
  - **A paid agent** runs in PassionCode's cloud inside its developer's organization (`org:<developer>`); each customer
@@ -108,6 +129,7 @@ secrets, its data, and a hub that can admit calls. The local Fabric hub is not a
108
129
  - Where does each instance run, and in which context? Write it in the knowledge pack.
109
130
  - Does any state, secret, log line or cache key cross contexts? It must not.
110
131
  - Which capabilities need the person's machine, and what do they answer when it is off?
132
+ - Which uptime category is it, A (a cloud host from the first release) or B (local only)? The README names it.
111
133
  - Who pays for each call (which grant, which key)? The person's personal key never pays for an organization.
112
134
  - If it joins machines or applies the estate record: tokens from stdin or a 0600 file only, revisions verified with
113
135
  their parent.
@@ -18,7 +18,7 @@ compatibility: >-
18
18
  this plugin; Codex/Kimi use command hooks; Hermes/OpenCode/OpenClaw/Pi use native context plugins after opt-in.
19
19
  metadata:
20
20
  author: PassionCode.ai
21
- version: "0.57.0"
21
+ version: "0.59.0"
22
22
  ---
23
23
 
24
24
  # Working with Fabric
@@ -58,7 +58,7 @@ SESSION_ID = re.compile(r"[A-Za-z0-9._:-]{1,128}")
58
58
  LOOPBACK_ORIGIN = re.compile(r"http://127\.0\.0\.1:(\d{1,5})")
59
59
 
60
60
 
61
- VERSION = "0.57.0"
61
+ VERSION = "0.59.0"
62
62
 
63
63
 
64
64
  HOSTS = {"claude": "Claude Code", "codex": "Codex", "kimi": "Kimi Code",