@passioncode-ai/passioncode 0.2.2 → 0.2.5

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 (24) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +35 -0
  3. package/family.json +1 -1
  4. package/package.json +1 -1
  5. package/payload/.claude-plugin/marketplace.json +2 -2
  6. package/payload/manifest.json +8 -8
  7. package/payload/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
  8. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +3 -3
  9. package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
  10. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +4 -3
  11. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/estate-record.md +35 -6
  12. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/lifecycle.md +58 -16
  13. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +1 -1
  14. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-estate.mjs +129 -43
  15. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-service.mjs +2 -1
  16. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_estate.py +192 -50
  17. package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_service.py +4 -3
  18. package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +3 -3
  19. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +1 -1
  20. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +4 -3
  21. package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session.py +1 -1
  22. package/payload/plugins/passioncode/.claude-plugin/plugin.json +1 -1
  23. package/payload/plugins/passioncode/hooks/repo-rules.js +9 -4
  24. package/payload/plugins/passioncode/skills/working-in-passioncode/SKILL.md +9 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.5 - 2026-10-15
4
+
5
+ ### Changed
6
+
7
+ - **Fabric Agent Adapter is pinned at `v0.33.0`** (it was `v0.32.1`). v0.33.0 pins the contract at `82e1636` and
8
+ reads contexts in its estate kits (DEC-0082): one agent placed on a machine for the person and for each organization
9
+ it serves, an organization's instance under a share grant of its context, and every estate rule total on hostile
10
+ JSON. It also fixes a flaky board test (FAA-43).
11
+
12
+ ## 0.2.4 - 2026-10-14
13
+
14
+ ### Changed
15
+
16
+ - **Fabric Agent Adapter is pinned at `v0.32.1`** (it was `v0.32.0`). v0.32.1 makes the
17
+ `building-fabric-services` lifecycle reference the rule its public readers follow: it states the lifecycle
18
+ itself (the supervisor owns the process, the descriptor, readiness) and links only public sources, where it used
19
+ to defer to the organization's private lifecycle contract (release iteration 1, N-12).
20
+ - 0.2.3 was not published on its own; this release carries its changes below.
21
+
22
+ ## 0.2.3 - 2026-10-14
23
+
24
+ ### Changed
25
+
26
+ - **The session-start line names Fabric principles 10 and 11.** In a passioncode-ai repository the
27
+ line now also says to work by principle 10, a free agent takes the next task (a wait goes on a
28
+ watcher, stop only at a human gate), and principle 11, a project moves in releases and every run
29
+ leaves a lesson the next one reads, each with its anchor in the knowledge base's `principles.md`.
30
+ The knowledge base's rules §4 relies on this line to reach every session, so an `AGENTS.md` need
31
+ not restate the rule (`test/repo-rules.test.js`).
32
+ - **`working-in-passioncode` §4 carries both principles** in two bullets with their links, and a
33
+ new scenario eval (`a-wait-is-not-a-stop`) holds the behaviour.
34
+ - **README: "With the ssheleg family"** — what the public sshlg-skills family is, how it relates to
35
+ this set (routers, the method, what each side adds, one channel per plugin id) and the two update
36
+ commands.
37
+
3
38
  ## 0.2.2 - 2026-10-14
4
39
 
5
40
  ### Changed
package/README.md CHANGED
@@ -141,6 +141,41 @@ turns automatic updates off. Unsupported flags are rejected before mutation.
141
141
  Repository docs describe the current source. See the [documentation index](docs/README.md)
142
142
  and dated handoffs for which changes have actually been published or installed.
143
143
 
144
+ ## With the ssheleg family
145
+
146
+ The ssheleg family ([sshlg-skills](https://github.com/ssheleg/sshlg-skills), on npm as
147
+ `sshlg-skills`) is a separate, public set of skills for coding agents: a working method with
148
+ `task-pipeline` (gated delivery and release iterations), `agent-sync` (leases and id
149
+ reservation), `super-ux`, `sheleg-design`, `make-skill`, `agent-stack` and others. It is not a
150
+ member of this launcher's set, and neither set installs or vendors the other's plugins. Install
151
+ both and update each as one set with its own command:
152
+
153
+ ```bash
154
+ npx @passioncode-ai/passioncode@latest update # this set: Fabric Agent Adapter, Observatory Log, passioncode
155
+ npx --yes sshlg-skills@latest update # the family; it takes no member argument
156
+ ```
157
+
158
+ - **Routers.** `npx sshlg-skills routers` writes one routing block into each agent's global
159
+ instructions (`~/.claude/CLAUDE.md`, `~/.codex/AGENTS.md`, `~/.gemini/GEMINI.md`, …). It says
160
+ which member owns which kind of work: interface behaviour, look, copy, delivery, coordination.
161
+ On a computer where Fabric is installed (its services directory exists) the block gains a
162
+ `fabric` router, which sends questions about the computer's agent services to the Fabric
163
+ tooling and to this set's Fabric Agent Adapter skills.
164
+ - **The method.** Fabric principle 11 (a project moves in releases, and every run leaves a lesson)
165
+ takes its method from `task-pipeline`'s release iteration. Principle 10's wait rule (a wait is
166
+ not a stop) reaches personal repositories as the family's opt-in rule block
167
+ (`npx sshlg-skills config set rules.wait on`). In a passioncode-ai repository both reach the
168
+ agent through this plugin's session-start line and `working-in-passioncode` instead.
169
+ - **What the family gains here.** Where Project Observatory runs, `task-pipeline` writes workflow
170
+ checkpoints and planned handoffs through it and records cross-project lessons in its shared
171
+ lesson store, as proposals; `agent-sync` takes the git leases that passioncode-ai repositories
172
+ declare.
173
+ - **One channel per plugin id.** Each plugin comes from one source: this set's from the
174
+ `passioncode` marketplace, each family member's from its own (`<name>@<name>`). A plain skill
175
+ copy in `~/.claude/skills` shadows a plugin; both updaters take such copies out of the way and
176
+ never create one. Never update a plugin member with a bare `npx skills update <name>`: it
177
+ recreates that copy.
178
+
144
179
  ## Release (maintainers)
145
180
 
146
181
  1. Tag every member at the version to ship; set `ref` to that tag in `family.json`.
package/family.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "name": "fabric-agent-adapter",
9
9
  "displayName": "Fabric Agent Adapter",
10
10
  "repo": "passioncode-ai/fabric-agent-adapter",
11
- "ref": "v0.32.0",
11
+ "ref": "v0.33.0",
12
12
  "kind": "plugin",
13
13
  "path": "plugins/fabric-agent-adapter",
14
14
  "legacyPluginIds": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passioncode-ai/passioncode",
3
- "version": "0.2.2",
3
+ "version": "0.2.5",
4
4
  "description": "Set up PassionCode.ai on a computer with one command — the apps from their verified releases, their MCP servers for every coding agent and the PassionCode.ai skills — and check and repair it with the same command or `doctor`.",
5
5
  "bin": {
6
6
  "passioncode": "bin/passioncode.js"
@@ -11,7 +11,7 @@
11
11
  "name": "fabric-agent-adapter",
12
12
  "displayName": "Fabric Agent Adapter",
13
13
  "source": "./plugins/fabric-agent-adapter",
14
- "version": "0.32.0",
14
+ "version": "0.33.0",
15
15
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
16
16
  "author": {
17
17
  "name": "PassionCode.ai",
@@ -35,7 +35,7 @@
35
35
  "name": "passioncode",
36
36
  "displayName": "PassionCode.ai",
37
37
  "source": "./plugins/passioncode",
38
- "version": "0.2.2",
38
+ "version": "0.2.5",
39
39
  "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
40
40
  "author": {
41
41
  "name": "PassionCode.ai",
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "family": "passioncode",
3
- "version": "0.2.2",
3
+ "version": "0.2.5",
4
4
  "release": true,
5
5
  "members": [
6
6
  {
7
7
  "name": "fabric-agent-adapter",
8
8
  "displayName": "Fabric Agent Adapter",
9
9
  "repo": "passioncode-ai/fabric-agent-adapter",
10
- "ref": "v0.32.0",
11
- "commit": "9078be6478029e8d9fe37855e8034c81f45ef3b3",
12
- "version": "0.32.0",
10
+ "ref": "v0.33.0",
11
+ "commit": "3ee30276f74cdfd8e6ef621d2b6595d6d69748be",
12
+ "version": "0.33.0",
13
13
  "via": "clone of https://***@github.com/passioncode-ai/fabric-agent-adapter.git",
14
14
  "skills": [
15
15
  "adapting-projects-to-fabric",
@@ -23,7 +23,7 @@
23
23
  "legacyMarketplaces": [
24
24
  "fabric-agent-adapter"
25
25
  ],
26
- "contentHash": "sha256:86290c04a4dcdefb5ee2fca424a363195947ded890e0ad97335d79fe4051b181",
26
+ "contentHash": "sha256:20176fde8edd9efa9acc380d9de778e8768e34d8ce42ce153a3d927c7f03739d",
27
27
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
28
28
  "author": {
29
29
  "name": "PassionCode.ai",
@@ -62,9 +62,9 @@
62
62
  "name": "passioncode",
63
63
  "displayName": "PassionCode.ai",
64
64
  "repo": "passioncode-ai/passioncode",
65
- "ref": "v0.2.2",
65
+ "ref": "v0.2.5",
66
66
  "commit": null,
67
- "version": "0.2.2",
67
+ "version": "0.2.5",
68
68
  "via": "this repository",
69
69
  "skills": [
70
70
  "setting-up-passioncode",
@@ -72,7 +72,7 @@
72
72
  ],
73
73
  "legacyPluginIds": [],
74
74
  "legacyMarketplaces": [],
75
- "contentHash": "sha256:f2bb2ec0982bd940e6d24746295692c7e84a1488a0298d3f9977c312ad8bdd7a",
75
+ "contentHash": "sha256:4c120907a056fa3ce5726653be9eaf014db01b10974decf4a87498b119b1b225",
76
76
  "description": "Set up and check PassionCode.ai on a computer with one command (setting-up-passioncode), the organisation's working rules for every contributor's agent (working-in-passioncode), and a once-a-day check that keeps the PassionCode.ai set current.",
77
77
  "author": {
78
78
  "name": "PassionCode.ai",
@@ -3,7 +3,7 @@
3
3
  "name": "fabric-agent-adapter",
4
4
  "displayName": "Fabric Agent Adapter",
5
5
  "description": "Create new Fabric-compatible agents, build them as always-alive local services with dashboards (fabric-service/0.1), and adapt existing projects to the Fabric Agent Contract through evidence-backed MCP, A2A, or local-runner provider bundles; and teach a coding agent to work in a project through Fabric (fabric-mcp, fabric_whoami, claim and release), with Claude Code session hooks.",
6
- "version": "0.32.0",
6
+ "version": "0.33.0",
7
7
  "author": {
8
8
  "name": "PassionCode.ai",
9
9
  "url": "https://passioncode.ai/"
@@ -5,9 +5,9 @@ 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.
6
6
  metadata:
7
7
  author: PassionCode.ai
8
- version: "0.32.0"
8
+ version: "0.33.0"
9
9
  contract-version: "0.1.0"
10
- contract-commit: "1636fa97262f4a26e2086572d540c2ee701e0010"
10
+ contract-commit: "82e163665e156a08aa3b92e4711015582c06162c"
11
11
  ---
12
12
 
13
13
  # Adapting projects to Fabric
@@ -70,7 +70,7 @@ Use exactly:
70
70
 
71
71
  - contract version `0.1.0`;
72
72
  - repository `https://github.com/passioncode-ai/fabric-agent-contract`;
73
- - commit `1636fa97262f4a26e2086572d540c2ee701e0010`.
73
+ - commit `82e163665e156a08aa3b92e4711015582c06162c`.
74
74
 
75
75
  This is the default for new bundles. When checking an issued legacy bundle, preserve its
76
76
  explicit supported lock and use that exact clean checkout. Never silently upgrade or
@@ -21,10 +21,10 @@ import fabric_secrets as _secrets # noqa: E402 - the contract's credential dete
21
21
 
22
22
  CONTRACT_VERSION = "0.1.0"
23
23
  CONTRACT_REPOSITORY = "https://github.com/passioncode-ai/fabric-agent-contract"
24
- CONTRACT_COMMIT = "1636fa97262f4a26e2086572d540c2ee701e0010"
24
+ CONTRACT_COMMIT = "82e163665e156a08aa3b92e4711015582c06162c"
25
25
  # #region supported-contract-revisions — docs: README.md#contract-pin
26
26
  # Only this declaration may name a legacy revision in live files.
27
- SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
27
+ SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "1636fa97262f4a26e2086572d540c2ee701e0010", "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
28
28
  # #endregion supported-contract-revisions
29
29
  INTEROP_KEY = "https://fabric.passioncode.ai/agent-contract/extensions/interop/0.1"
30
30
  MCP_REVISION = "2026-07-28"
@@ -18,10 +18,10 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
18
18
  compatibility: Python 3.9+ or Node.js 20+ for the kits; the probe needs Python 3.9+. launchd steps are macOS-only; Linux services install a systemd user unit and Windows ones a per-user Scheduled Task with the kits' writers. Dashboard handoff optionally uses Fabric Dashboards MCP link/host_status/open; without it, report unresolved host capability. The contract checkout is optional.
19
19
  metadata:
20
20
  author: PassionCode.ai
21
- version: "0.32.0"
21
+ version: "0.33.0"
22
22
  contract-version: "0.1.0"
23
23
  extension: "fabric-service/0.1"
24
- extension-commit: "1636fa97262f4a26e2086572d540c2ee701e0010"
24
+ extension-commit: "82e163665e156a08aa3b92e4711015582c06162c"
25
25
  ---
26
26
 
27
27
  # Building Fabric services
@@ -86,7 +86,8 @@ are in [`scripts/fabric_interop.py`](scripts/fabric_interop.py) and
86
86
  service checks each call's delegation token (DEC-0039): read [the delegation reference](references/delegation.md);
87
87
  the verifier is [`scripts/fabric_delegation.py`](scripts/fabric_delegation.py) /
88
88
  [`scripts/fabric-delegation.mjs`](scripts/fabric-delegation.mjs). A service on the operator's machines that
89
- applies the estate record (DEC-0055) verifies each revision first and trusts only the keys it certifies: read
89
+ applies the estate record (DEC-0055) verifies each revision first and trusts only the keys it certifies, and runs an
90
+ agent once per context, the person's or an organization's (DEC-0082): read
90
91
  [the estate record reference](references/estate-record.md). When Fabric chooses the service's runner and models
91
92
  (DEC-0046), or a project routes its work through an ordered list of runners (DEC-0026: the doctor's closed probe
92
93
  results, the route checks FAC-SEM-028…034), read [the profiles reference](references/profiles.md).
@@ -1,10 +1,11 @@
1
1
  # The estate record — what the operator's machines are, signed by their passkey (DEC-0055)
2
2
 
3
3
  Fabric Agent Contract DEC-0055, `docs/specification/estate-record.md`, schema `schemas/estate-record.schema.json`,
4
- rules `FAC-SEM-072…076`. One revision of one person's estate lists:
4
+ rules `FAC-SEM-072…076`, with contexts (DEC-0082, `docs/specification/contexts.md`, `FAC-SEM-110`, `FAC-SEM-111`).
5
+ One revision of one person's estate lists:
5
6
  - the passkeys that sign it;
6
7
  - the machines, with their device keys and a certificate of each machine's hub key;
7
- - the packages placed on each machine;
8
+ - the packages placed on each machine, each in a context: the person's own, or an organization's;
8
9
  - where each secret slot is filled from (by reference only);
9
10
  - share grants and revocations.
10
11
 
@@ -16,6 +17,7 @@ derived from its own digest. Python: [`scripts/fabric_estate.py`](../scripts/fab
16
17
 
17
18
  - Applying a revision
18
19
  - Trusting a delegation key
20
+ - Contexts: one agent for you and for an organization
19
21
  - Checking a record you produce
20
22
  - What the kit does not do
21
23
 
@@ -51,7 +53,8 @@ It checks, in the order of the ER table:
51
53
  - The signature: ES256 or EdDSA, under a key validated first (a valid P-256 point; an Ed25519 key of full order).
52
54
  7. **The window.** At most 30 days. A revision may be issued up to 60 s ahead of the verifier's clock (DEC-0066,
53
55
  `CLOCK_SKEW_SECONDS`); earlier than that it is `not-yet-valid`.
54
- 8. **The record's own rules.** A violation is `rule-violation` with its `codes`, `FAC-SEM-072…076` and `FAC-SEM-092`
56
+ 8. **The record's own rules.** A violation is `rule-violation` with its `codes`, `FAC-SEM-072…076`, `FAC-SEM-110`,
57
+ `FAC-SEM-111` and `FAC-SEM-092`
55
58
  (each passkey the record lists is a signing credential, its origins within its RP ID).
56
59
 
57
60
  Dates are strict RFC 3339: a `T`, an offset, and a day that exists. `:60` and a space are refused. Passkey JWK `x`
@@ -90,14 +93,40 @@ gate = fd.delegation_gate(audience=..., jwks=keys, issuer=..., effects=..., esta
90
93
  `{certified: true, machine, revision}` or `{certified: false, fault, detail}`. Both kits are held to the contract's
91
94
  `fixtures/vectors/hub-key.json` (`test/fixtures/contract/hub-key-vectors.json`).
92
95
 
96
+ ## Contexts: one agent for you and for an organization
97
+
98
+ A placement and a secret placement carry a `context` (DEC-0082): `personal`, or `org:<orgId>` with an organization id
99
+ of `[A-Za-z0-9][A-Za-z0-9_-]{0,127}`. Absent is `personal`; `null` is not absent. A context is compared as written:
100
+ `Personal`, `ORG:x` or a trailing newline names no context. `parse_context(value)` / `parseContext(value)` answer
101
+ `{kind: "personal"}`, `{kind: "org", org}` or nothing.
102
+
103
+ - **One agent, once per context.** A machine may place the same `agentRef` for the person and for each organization;
104
+ placements are keyed by agent and context (FAC-SEM-074), and a secret slot by agent, slot and context (FAC-SEM-075).
105
+ A secret fills a slot of an agent placed on that machine in the secret's own context.
106
+ - **An organization's instance runs under a share grant** (FAC-SEM-111). Its placement names, in `grant`, a share grant
107
+ of the record whose `context` is the placement's, as written, and that the record does not revoke. A personal
108
+ placement names no grant. A context that is not `org:<orgId>` matches no grant, so it fails closed.
109
+ - **An organization's slot is filled from its own machine.** An envelope names no context, so a secret placed in an
110
+ organization's context has `from` equal to the machine it sits on (FAC-SEM-075).
111
+ - **A share grant is never personal** (FAC-SEM-110). A grant to a principal outside the estate (`principalKind:
112
+ external`) is used in that principal's organization; `context: "personal"` on it is a finding, and an absent one
113
+ means not stated. `grant_context_findings(grant, at)` / `grantContextFindings(grant, at)` check one grant on its own.
114
+
115
+ Every rule is total on hostile JSON, as the contract's are: an `instancePath` names an item by its index as written (a
116
+ non-object before it does not shift it), and a value whose own `toString` member is data is shown as its JSON. Both kits
117
+ answer the contract's contexts fixtures and variants of them (`test/fixtures/contract/estate-vectors.json`).
118
+
93
119
  ## Checking a record you produce
94
120
 
95
121
  `estate_record_rules(kind, value)` / `estateRecordRules(kind, value)` are the contract's rules, finding for finding:
96
- - `estate-record` (FAC-SEM-072…076);
122
+ - `estate-record` (FAC-SEM-072…076, 110, 111);
97
123
  - `estate-record-revision` `{parent, next}` (072);
98
- - `estate-placement` `{record, machine, package}` (074, 075).
124
+ - `estate-placement` `{record, machine, package, context?}` (074, 075, 111): the package placed on that machine in that
125
+ context, `personal` when `context` is absent.
99
126
 
100
- `placement_trust(record, machine, agentRef)` names the publisher keys a package verifier trusts for a placed package.
127
+ `placement_trust(record, machine, agentRef, context="personal")` / `placementTrust(record, machine, agentRef, context)`
128
+ names the publisher keys a package verifier trusts for the package placed in that context. A caller that passes no
129
+ context gets the personal placement, as before contexts.
101
130
 
102
131
  ## What the kit does not do
103
132
 
@@ -1,31 +1,73 @@
1
1
  # Lifecycle — supervisor, install, release layout, state
2
2
 
3
- This page implements the organization's
4
- [product lifecycle contract](https://github.com/passioncode-ai/fabric-workspace/blob/main/knowledge/lifecycle.md)
5
- (rules LC-01…LC-15) for `fabric-service/0.1` services. Where the two differ, the contract
6
- wins and this page is the defect. The rule each section serves:
7
-
8
- | Contract rule | Here | Kit (Python / Node) |
9
- |---|---|---|
10
- | LC-01 quit reaches exit within a deadline | [Shutdown](#shutdown) | `Drain` / `Drain` |
11
- | LC-03 a job is bounded and exclusive | [one copy under a supervisor](#one-copy-under-a-supervisor) | `hold_single_instance` / `holdSingleInstance` |
12
- | LC-09 the footprint is declared | the descriptor and the plist, unit or task; the service's own `AGENTS.md` names its label (unit, task) and port | `write_descriptor`, `launchd_plist`, `systemd_unit` / `systemdUnit`, `task_xml` / `taskXml` |
13
- | LC-11 previous release kept, older pruned | [Install, upgrade, uninstall](#install-upgrade-uninstall) | `prune_releases` / `pruneReleases` |
14
- | LC-12 every file is bounded | [Logs](#logs) | `RotatingLog`, `cap_stdout_log` / `RotatingLog`, `capStdoutLog` |
15
- | LC-14 operator intent wins; uninstall is symmetric | [Install, upgrade, uninstall](#install-upgrade-uninstall), [Linux and Windows](#linux-and-windows) | `launchd_install` / `launchdInstall`, `launchd_uninstall` / `launchdUninstall`; `systemd_install` / `systemdInstall`, `task_install` / `taskInstall` and their uninstalls |
16
- | LC-15 builds clean up after themselves | release pruning runs inside the installer, not later | `prune_releases` / `pruneReleases` |
3
+ **This page is the rule** for anyone who builds, installs or runs a `fabric-service/0.1` service with this
4
+ skill. Everything you must follow is stated here or in the public Fabric Agent Contract's
5
+ [service specification, *Lifecycle*](https://github.com/passioncode-ai/fabric-agent-contract/blob/main/docs/specification/service.md#lifecycle), which this page
6
+ implements and links; where the two disagree, the specification is right and this page is the defect (report it in
7
+ this repository). Inside
8
+ PassionCode.ai the same rules make up the organization's product lifecycle contract, kept in its knowledge base. The
9
+ `LC-` ids below are that contract's, given for provenance only: nothing behind them is needed to follow this page.
17
10
 
18
11
  ## Contents
19
12
 
20
- - [launchd plist](#launchd-plist-generate-it-with-launchd_plist)
13
+ - [The rules](#the-rules)
14
+ - [launchd plist](#launchd-plist-generate-it-with-launchd_plist-or-launchdplist-both-kits-write-the-same-bytes)
21
15
  - [Install, upgrade, uninstall](#install-upgrade-uninstall)
22
16
  - [One copy under a supervisor](#one-copy-under-a-supervisor)
17
+ - [An app's own descriptor and the node](#an-apps-own-descriptor-and-the-node)
23
18
  - [Directories](#directories)
24
19
  - [Logs](#logs)
25
20
  - [Shutdown](#shutdown)
26
21
  - [Being managed by a host lifecycle broker](#being-managed-by-a-host-lifecycle-broker)
27
22
  - [Linux and Windows](#linux-and-windows)
28
23
 
24
+ ## The rules
25
+
26
+ **The supervisor owns the process.** A local service runs under exactly one supervisor, named in its descriptor's
27
+ `lifecycle.manager`: `launchd` (macOS), `systemd` (Linux), `task-scheduler` (Windows) or `node` (Project
28
+ Observatory's node supervising its children). An app a person starts says `person` and has no supervisor; where a host
29
+ runs a lifecycle broker, the broker is the one that quits it ([below](#being-managed-by-a-host-lifecycle-broker)).
30
+ Starting, stopping and restarting go through the supervisor and nothing else:
31
+
32
+ - **Never kill a service.** A host, an agent, an installer or the service's own CLI never starts a service process
33
+ directly and never kills or signals one. It asks the supervisor: on macOS `bootout` + `disable` to stop and keep
34
+ off, `enable` + `bootstrap` to start, `kickstart -k` to restart; [Linux and Windows](#linux-and-windows) have
35
+ their own verbs; a node's child is started, stopped and restarted only through the node.
36
+ - **State comes from the supervisor**: `launchctl print`, `systemctl --user show`, the task's state and last run,
37
+ or the node's record. Compare the supervisor's pid with the well-known `process.pid` to see a second copy.
38
+ - **Restart is the supervisor's.** A service that exits is started again, no sooner than 10 s after its last start
39
+ (`KeepAlive` with `ThrottleInterval` 10, `Restart=always` with `RestartSec=10`, the node likewise; a scheduled task
40
+ restarts on failure every minute). A restart onto new code shows as a new `service.build`.
41
+ - **Off stays off.** The operator's off survives a login, an upgrade and a reinstall (LC-14).
42
+
43
+ **The descriptor is the installation.** The installer writes one descriptor per instance (`<id>.<instance>.json`,
44
+ atomic, mode 0600) and the uninstaller removes it; a running service never writes its own, except an app
45
+ ([below](#an-apps-own-descriptor-and-the-node)). It names the supervisor and its file (`label` and `plist`, `unit`,
46
+ or `task`; none for `node` and `person`), the `origin` `http://127.0.0.1:<port>` (a port is a claim: an installer
47
+ refuses a port another instance declares), the service's directories, and commands as argument arrays that never
48
+ carry a credential. Only `doctor` and `update` are commands a host may run.
49
+
50
+ **Started is not ready.** Readiness is the well-known document, `GET /.well-known/fabric-service`: answered without
51
+ authentication, from memory, in under 100 ms, with `service.id` and `service.instance` equal to the descriptor's.
52
+ `status` is `starting`, `ready`, `degraded` or `stopping`; `degraded` is always present, and an empty list asserts
53
+ full health. A process that cannot serve does not answer, and a host derives `down`. An install or upgrade polls
54
+ that document until `service.id` matches (at most 40 s) before it calls the service started.
55
+
56
+ | Id | Rule | Here | Kit (Python / Node) |
57
+ |---|---|---|---|
58
+ | LC-01 | **Quit reaches exit within a deadline.** Every trigger (`SIGTERM`, `SIGINT`, logout, the app's own quit) ends the process: within 5 s idle, within 10 s with work in flight, then a hard exit. Nothing starts after quit begins | [Shutdown](#shutdown) | `Drain` / `Drain` |
59
+ | LC-02 | **Every child has an owner, a deadline and a process group.** No subprocess without a timeout; on timeout or the parent's exit the whole group goes; no orphan survives. Locks and shared resources are released in `finally` | your own code | — |
60
+ | LC-03 | **A job is bounded and exclusive.** One copy holds `service.lock`; a scheduled job has a watchdog shorter than its interval, shares its lock with every manual entry point, bounds every network call and records each run's outcome. Long work is journalled before it starts | [One copy under a supervisor](#one-copy-under-a-supervisor) | `hold_single_instance` / `holdSingleInstance` |
61
+ | LC-04 | **Credentials are silent after setup.** No background prompt; a credential read that is denied or absent is cached and backs off until a person acts | [Directories](#directories) (secret files 0600) | — |
62
+ | LC-05 | **Identity is stable across updates.** The supervisor runs a stable executable inside a release directory, never an interpreter by a versioned package-manager path; signed builds are pinned by team and identifier | [launchd plist](#launchd-plist-generate-it-with-launchd_plist-or-launchdplist-both-kits-write-the-same-bytes) | — |
63
+ | LC-09 | **The footprint is declared.** The service's `AGENTS.md` names every resident process, its supervisor label, unit or task and its port, who starts each and who stops it | the descriptor and the supervisor file | `write_descriptor`, `launchd_plist`, `systemd_unit` / `systemdUnit`, `task_xml` / `taskXml` |
64
+ | LC-11 | **An update is a lifecycle event.** An upgrade writes a new immutable release and restarts through the supervisor; the previous release is kept for rollback, older ones are pruned | [Install, upgrade, uninstall](#install-upgrade-uninstall) | `prune_releases` / `pruneReleases` |
65
+ | LC-12 | **Every file is bounded.** Logs rotate by size (5 × 5 MB), mode 0600, never in `/tmp`; temporary leftovers are swept at start | [Logs](#logs) | `RotatingLog`, `cap_stdout_log` / `RotatingLog`, `capStdoutLog` |
66
+ | LC-14 | **The operator's intent wins; uninstall is symmetric.** No install or upgrade re-enables what the operator turned off; uninstall removes what install added (supervisor file, override, descriptor), and data goes only on an explicit purge | [Install, upgrade, uninstall](#install-upgrade-uninstall), [Linux and Windows](#linux-and-windows) | `launchd_install` / `launchdInstall`, `launchd_uninstall` / `launchdUninstall`; `systemd_install` / `systemdInstall`, `task_install` / `taskInstall` and their uninstalls |
67
+ | LC-15 | **Builds clean up after themselves.** At most the current release and the one before it remain; the prune runs inside the installer, not later | [Install, upgrade, uninstall](#install-upgrade-uninstall) | `prune_releases` / `pruneReleases` |
68
+ | LC-21 | **A secret never travels in a command line.** Keys reach the process through its environment, a 0600 file or a vault, never argv; a supervisor file carries no secret | [launchd plist](#launchd-plist-generate-it-with-launchd_plist-or-launchdplist-both-kits-write-the-same-bytes), [Linux and Windows](#linux-and-windows) | the supervisor-file writers refuse a secret-shaped value |
69
+ | LC-22 | **Work survives a restart.** Work started but not finished is journalled on disk, without secrets, before it starts; the restarted service takes it back or cleans it up | [Shutdown](#shutdown) | — (the service's own journal) |
70
+
29
71
  ## launchd plist (generate it with `launchd_plist` or `launchdPlist`; both kits write the same bytes)
30
72
 
31
73
  | Key | Value | Why |
@@ -168,7 +210,7 @@ has to survive being driven from outside:
168
210
 
169
211
  - **Quit reaches exit through both doors.** A broker asks the platform's own quit first — on
170
212
  macOS `NSRunningApplication.terminate()`, the path Cmd+Q and the Dock take — and `SIGTERM` a few
171
- seconds later. Handle both and exit inside the deadline (fabric-workspace LC-01). A desktop app
213
+ seconds later. Handle both and exit inside the deadline (LC-01). A desktop app
172
214
  that answers `SIGTERM` by closing its window and staying alive is stopped late or not at all.
173
215
  - **A background launch stays in the background.** A broker opens the app non-activating
174
216
  (`NSWorkspace.OpenConfiguration.activates = false`); do not call `app.focus()`, `show()` on a
@@ -1,6 +1,6 @@
1
1
  # fabric-service/0.1 — wire reference
2
2
 
3
- Pinned to `fabric-agent-contract` commit `1636fa97262f4a26e2086572d540c2ee701e0010`
3
+ Pinned to `fabric-agent-contract` commit `82e163665e156a08aa3b92e4711015582c06162c`
4
4
  (`docs/specification/service.md`, DEC-0015; the usage report DEC-0021; Windows and Linux supervision
5
5
  DEC-0032 and DEC-0033; the operator channel DEC-0034 and DEC-0035; the embedded dashboard DEC-0036;
6
6
  delegation and the operator session's lifetime DEC-0039; the meter's call window DEC-0041; session handoff DEC-0042;