@passioncode-ai/passioncode 0.2.25 → 0.2.27
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/CHANGELOG.md +18 -0
- package/family.json +1 -1
- package/package.json +1 -1
- package/payload/.claude-plugin/marketplace.json +2 -2
- package/payload/manifest.json +8 -8
- package/payload/plugins/fabric-agent-adapter/.claude-plugin/plugin.json +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/SKILL.md +3 -3
- package/payload/plugins/fabric-agent-adapter/skills/adapting-projects-to-fabric/scripts/adapt_project.py +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/SKILL.md +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md +3 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md +11 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md +5 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-session.mjs +5 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-board-shapes.mjs +18 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-descriptor.mjs +1 -1
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop-rules.mjs +3 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-interop.mjs +7 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric-protocols.mjs +4 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_session.py +6 -3
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_board_shapes.py +25 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_descriptor.py +2 -2
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop.py +3 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_interop_rules.py +4 -0
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/fabric_protocols.py +4 -4
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/kit.json +242 -14
- package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/scripts/share-registration.schema.json +3 -2
- package/payload/plugins/fabric-agent-adapter/skills/creating-fabric-agents/SKILL.md +3 -3
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/SKILL.md +11 -6
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/references/project-board.md +94 -31
- package/payload/plugins/fabric-agent-adapter/skills/working-with-fabric/scripts/fabric_session_env.py +1 -1
- package/payload/plugins/passioncode/.claude-plugin/plugin.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.27 - 2026-10-23
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Fabric Agent Adapter is pinned at `v0.46.0`** (it was `v0.45.0`; `v0.45.1` is included): `working-with-fabric`
|
|
8
|
+
teaches what to do when nobody answers a board request and reads the board as Fabric 0.3.9 serves it, and the kit is
|
|
9
|
+
pinned to the Fabric Agent Contract's `0a4ce77` (a node's call queue: `queue-full`, three new protocols). Notes are
|
|
10
|
+
in the adapter's CHANGELOG.
|
|
11
|
+
|
|
12
|
+
## 0.2.26 - 2026-10-23
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- **Fabric Agent Adapter is pinned at `v0.45.0`** (it was `v0.44.2`): pinned to the Fabric Agent Contract's
|
|
17
|
+
`e742016`; the board kit reads the board as Fabric 0.3.9 serves it (the `answered` state, the read counts, a request
|
|
18
|
+
nobody can answer now), a peer may be `processor-hosted`, and three new protocols are known. Notes are in the
|
|
19
|
+
adapter's CHANGELOG.
|
|
20
|
+
|
|
3
21
|
## 0.2.25 - 2026-10-22
|
|
4
22
|
|
|
5
23
|
### Changed
|
package/family.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passioncode-ai/passioncode",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.27",
|
|
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.
|
|
14
|
+
"version": "0.46.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.
|
|
38
|
+
"version": "0.2.27",
|
|
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",
|
package/payload/manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"family": "passioncode",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.27",
|
|
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.
|
|
11
|
-
"commit": "
|
|
12
|
-
"version": "0.
|
|
10
|
+
"ref": "v0.46.0",
|
|
11
|
+
"commit": "ef54ebb4b232cc1a491d44b88f689b1400d56b79",
|
|
12
|
+
"version": "0.46.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:
|
|
26
|
+
"contentHash": "sha256:1c66e0ecef3b0cf6b4c835f51b0b7db758a1be268885aaa5c7303206319d8865",
|
|
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.
|
|
65
|
+
"ref": "v0.2.27",
|
|
66
66
|
"commit": null,
|
|
67
|
-
"version": "0.2.
|
|
67
|
+
"version": "0.2.27",
|
|
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:
|
|
75
|
+
"contentHash": "sha256:cdd7356ff066cbb1424530a64bddeeb6ab25c4b6cbbcc9cf1b90fe7b1e590a55",
|
|
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.
|
|
6
|
+
"version": "0.46.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.
|
|
8
|
+
version: "0.46.0"
|
|
9
9
|
contract-version: "0.1.0"
|
|
10
|
-
contract-commit: "
|
|
10
|
+
contract-commit: "0a4ce773c4b9e6bec40c6387eb3f988af7152c0e"
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# Adapting projects to Fabric
|
|
@@ -76,7 +76,7 @@ Use exactly:
|
|
|
76
76
|
|
|
77
77
|
- contract version `0.1.0`;
|
|
78
78
|
- repository `https://github.com/passioncode-ai/fabric-agent-contract`;
|
|
79
|
-
- commit `
|
|
79
|
+
- commit `0a4ce773c4b9e6bec40c6387eb3f988af7152c0e`.
|
|
80
80
|
|
|
81
81
|
This is the default for new bundles. When checking an issued legacy bundle, preserve its
|
|
82
82
|
explicit supported lock and use that exact clean checkout. Never silently upgrade or
|
|
@@ -22,10 +22,10 @@ import fabric_secrets as _secrets # noqa: E402 - the contract's credential dete
|
|
|
22
22
|
|
|
23
23
|
CONTRACT_VERSION = "0.1.0"
|
|
24
24
|
CONTRACT_REPOSITORY = "https://github.com/passioncode-ai/fabric-agent-contract"
|
|
25
|
-
CONTRACT_COMMIT = "
|
|
25
|
+
CONTRACT_COMMIT = "0a4ce773c4b9e6bec40c6387eb3f988af7152c0e"
|
|
26
26
|
# #region supported-contract-revisions — docs: README.md#contract-pin
|
|
27
27
|
# Only this declaration may name a legacy revision in live files.
|
|
28
|
-
SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "c55f2d5a11c9a0197e958e87212fca7ae1154769", "07042181bfae2a481aa0da1a5651f24c76669c48", "d8146cfb0a1c74fc6432284c4356bb0c864b37b5", "82e163665e156a08aa3b92e4711015582c06162c", "1636fa97262f4a26e2086572d540c2ee701e0010", "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
|
|
28
|
+
SUPPORTED_CONTRACT_COMMITS = (CONTRACT_COMMIT, "e742016dc396aa31e5a13a893489e43f15786c69", "bfa9a67aa3c6cb808c093e7d83cd59799f1ee7d0", "c55f2d5a11c9a0197e958e87212fca7ae1154769", "07042181bfae2a481aa0da1a5651f24c76669c48", "d8146cfb0a1c74fc6432284c4356bb0c864b37b5", "82e163665e156a08aa3b92e4711015582c06162c", "1636fa97262f4a26e2086572d540c2ee701e0010", "f00a7685e3861c27f7899c033fb850a145a1708f", "a046f57cf46ea9e3da57ff8ce6a3ed4d26d8774b", "fe55fc67cba1a9e34a736da7d148d4fe2a8fbfa0", "b291bf09e0a9f9fd3c16cf540832cd021203b7d1", "0cacd08b0e95b6abf131eae1206b60792138bc3c", "2eb91c00e852d9c31d804310d38fad91589fcbac", "8e49fbd5fbbf230e32c1969cd24ac3c22422f0a5", "a83bec6b47776af688402c18efec507143061f68", "52da526d7cc1e063d74c5b520dbc03d0ace15246", "78b2da018d2c4bd9ac1bde1dc3b7f1a110a175f5", "623bf61358c339cb10297807b3f024b5d9f1f327", "23f9fda4c05f8a3852246ee98d4f2adf74ed0875", "9091d3d6606b0c4b5591a38c3674356a01fd7f55", "df55c8c54a23251342a7ee57ba95642b7eb39e61", "2ce392291c6668598d12cd38327e24696b5ca15c")
|
|
29
29
|
# #endregion supported-contract-revisions
|
|
30
30
|
INTEROP_KEY = "https://fabric.passioncode.ai/agent-contract/extensions/interop/0.1"
|
|
31
31
|
MCP_REVISION = "2026-07-28"
|
|
@@ -14,10 +14,10 @@ license: AGPL-3.0-only OR LicenseRef-PassionCode-Commercial
|
|
|
14
14
|
compatibility: Python 3.9+ or Node.js 20+ for the kits (Node.js 22+ for a Linux service's instance lock); 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.
|
|
15
15
|
metadata:
|
|
16
16
|
author: PassionCode.ai
|
|
17
|
-
version: "0.
|
|
17
|
+
version: "0.46.0"
|
|
18
18
|
contract-version: "0.1.0"
|
|
19
19
|
extension: "fabric-service/0.1"
|
|
20
|
-
extension-commit: "
|
|
20
|
+
extension-commit: "0a4ce773c4b9e6bec40c6387eb3f988af7152c0e"
|
|
21
21
|
---
|
|
22
22
|
|
|
23
23
|
# Building Fabric services
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/board.md
CHANGED
|
@@ -117,7 +117,9 @@ spend sees it) is the service's own responsibility. A message never carries an a
|
|
|
117
117
|
| `idempotency_conflict` | a key sent with other content is a defect, not an outage: `board.blocked` (notify) naming it, and the session is left |
|
|
118
118
|
| in progress at a reconnect whose slot another session still holds | marked `unknown`; reconciled every five minutes once the slot is this session's |
|
|
119
119
|
|
|
120
|
-
On restart the checkpoint is read back: settled or
|
|
120
|
+
On restart the checkpoint is read back: settled, queued, claimed or `answered` requests are dropped (`answered`:
|
|
121
|
+
the target Project's participant reply answered it, DEC-0098 and DEC-0102; not settled, the requester may still cancel
|
|
122
|
+
it, but no longer this responder's); `in_progress` and `outcome_unknown` are
|
|
121
123
|
reconciled from what `observe(entry)` answers (observed only; with no observation they stay unknown and notify);
|
|
122
124
|
accepted ones go back to this responder with `com.responder_replace {expectedGeneration, transfer}`, whose receipt's
|
|
123
125
|
`transferred` (DEC-0078: `{request, revision, attempt}`) carries the new fence. **A Fabric that does not answer
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/interop.md
CHANGED
|
@@ -120,6 +120,17 @@ As a caller: send `request_meta(traceparent)` as your `_meta` (it names `fabric-
|
|
|
120
120
|
(`jobs.quotaExhausted(id, message, {retryable, resetsAt})`, `jobCallerAction`, `intentIdempotencyKey`). The guidance
|
|
121
121
|
for an agent's own design is in `creating-fabric-agents/references/resilience-and-autonomy.md`.
|
|
122
122
|
|
|
123
|
+
**A node's call queue (DEC-0104).** A provider that queues refuses a start past its queue with `queue-full`: an `isError`
|
|
124
|
+
result with one text item whose JSON is `{error: {code: "queue-full", message, data: {retryable, retryAfterSeconds}}}`,
|
|
125
|
+
never `structuredContent`; the caller starts again after `retryAfterSeconds` under the same key. Read that refusal
|
|
126
|
+
from the start's own `isError` result, before any answer reaches `job_caller_action` / `jobCallerAction`: those read a
|
|
127
|
+
job, and `queue-full` never ends one, so they answer a job it ended `invalid` (`QUEUE_FULL` names the code, and the
|
|
128
|
+
kit's job store refuses to end a job with it). The kit has no reader for the refusal yet (FAA-103). Through Fabric's hub
|
|
129
|
+
a caller today sees `product-error` instead, kept under the key for 24 hours, until the hub relays `queue-full`
|
|
130
|
+
(Fabric CO-376). The kit does not queue: it declares no load extension (`extensions/load/0.1`) and serves no
|
|
131
|
+
`fabric.job.list`, so its `unknown-job` and repeated keys keep the rules above. A node that will queue needs
|
|
132
|
+
`jobCallerKey`, the contract's `agent-network.json` vectors and the load report first (FAA-103).
|
|
133
|
+
|
|
123
134
|
## What the probe checks
|
|
124
135
|
|
|
125
136
|
`check_service.py` adds these rules; they read, and never call a capability.
|
package/payload/plugins/fabric-agent-adapter/skills/building-fabric-services/references/protocol.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# fabric-service/0.1: wire reference
|
|
2
2
|
|
|
3
|
-
Pinned to `fabric-agent-contract` commit `
|
|
3
|
+
Pinned to `fabric-agent-contract` commit `0a4ce773c4b9e6bec40c6387eb3f988af7152c0e`
|
|
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;
|
|
@@ -9,7 +9,9 @@ DEC-0045; runner and model profiles DEC-0046; the estate record DEC-0055; one co
|
|
|
9
9
|
the Windows token-file reader DEC-0057; the hub key the estate certifies DEC-0060; signing credentials DEC-0063; the
|
|
10
10
|
estate record's 60 s skew DEC-0066; an app's own descriptor DEC-0069; the descriptor for listings DEC-0061; identifier equality
|
|
11
11
|
by normal form DEC-0076; the project board's directory page DEC-0077 and a replacement's transferred attempts DEC-0078; an operator's hold DEC-0079, a structured finding DEC-0080 and the owner's settlement DEC-0081; `fabric-connections/0.1` in the protocol register
|
|
12
|
-
DEC-0096; a schema pattern never uses a bare dot DEC-0097
|
|
12
|
+
DEC-0096; a schema pattern never uses a bare dot DEC-0097; the board as Fabric 0.3.9 serves it DEC-0098 and DEC-0102, a
|
|
13
|
+
`processor-hosted` peer DEC-0099, three device protocols in the register DEC-0101; a call queue's `queue-full` and
|
|
14
|
+
the `agent-directory`, `fabric-load` and `service-load` protocols DEC-0104). The contract's schemas are normative;
|
|
13
15
|
this page is the working summary. Extension key:
|
|
14
16
|
`https://fabric.passioncode.ai/agent-contract/extensions/service/0.1`.
|
|
15
17
|
|
|
@@ -41,7 +43,7 @@ File `<id>.<instance>.json`, mode 0600 (on Windows a protected ACL), atomic.
|
|
|
41
43
|
| `source.repository`, `fabricManifest` | optional |
|
|
42
44
|
| `installedAt`, `installedBy` | when, and who installed it: the installer and its version as a string, or `{by, role?, tool}` (DEC-0061): `by` `person` (with `role` `operator` or `administrator`), `node` or `organization`; `role` for a person only; `tool` the installer and its version. It names a role, never a person. An app's descriptor uses the object form with `by: "person"`. `register_remote` / `registerRemote` and the sample write the object form |
|
|
43
45
|
| `runner` | optional `{kind?, credentialKind}` (DEC-0061): `credentialKind` `subscription`, `key`, `platform-metered` or `none`: a kind, never a credential (`FAC-SEM-086` refuses a credential-shaped value here or in `installedBy`); a subscription names its vendor's runner (`claude-code`, `kimi-code`, `codex`) |
|
|
44
|
-
| `peer` | optional `{endpoint, custody, residency, maxDataClass}` (DEC-0061): the service is offered to others. `endpoint` `https://<dns-name>[:<port>][/<path>]`, never a reserved name (`FAC-SEM-085`); `custody` `organization-managed` or `
|
|
46
|
+
| `peer` | optional `{endpoint, custody, residency, maxDataClass}` (DEC-0061): the service is offered to others. `endpoint` `https://<dns-name>[:<port>][/<path>]`, never a reserved name (`FAC-SEM-085`); `custody` `organization-managed`, `personal` or `processor-hosted` (an organization's cloud a processor runs under a processing agreement, DEC-0099); `residency` 1–32 ISO 3166-1 alpha-2 codes, `EU` admitted, user-assigned codes refused; `maxDataClass` `public` < `internal` < `confidential` < `restricted`. An offer needs `runner` and never runs on a subscription. A host sends a request from anyone but the operator only when `peer_refusal` / `peerRefusal` answers null (`FAC-SEM-088`); usage rows may take `route: "platform-metered"` |
|
|
45
47
|
|
|
46
48
|
## Identifier equality (DEC-0076)
|
|
47
49
|
|
|
@@ -22,6 +22,9 @@ export const EVENT_SCHEMA = 'urn:fabric:schema:board-responder-event:1';
|
|
|
22
22
|
export const STOP_CODES = Object.freeze(new Set(['fenced', 'lease_expired', 'authority_changed']));
|
|
23
23
|
|
|
24
24
|
const TERMINAL = new Set(['completed', 'failed_known', 'cancelled', 'expired']);
|
|
25
|
+
// No longer this responder's to work on: back in the queue, claimed again, or answered by the target Project's
|
|
26
|
+
// participant reply (DEC-0098, DEC-0102; not terminal, the requester may still cancel it, and nobody claims it).
|
|
27
|
+
const NOT_OURS = new Set(['queued', 'claimed', 'answered']);
|
|
25
28
|
|
|
26
29
|
/** A lost call is sent again this long before the lease it keeps would lapse (FAA-77, finding 38). */
|
|
27
30
|
export const LEASE_MARGIN_SECONDS = 10;
|
|
@@ -226,7 +229,7 @@ export class Responder {
|
|
|
226
229
|
}
|
|
227
230
|
throw e;
|
|
228
231
|
}
|
|
229
|
-
if (state === null || TERMINAL.has(state) || state
|
|
232
|
+
if (state === null || TERMINAL.has(state) || NOT_OURS.has(state)) this.checkpoint.drop(entry.request);
|
|
230
233
|
else if (state === 'accepted') (transfer[entry.capability] ??= []).push(entry);
|
|
231
234
|
else reconcile.push(entry);
|
|
232
235
|
}
|
|
@@ -348,7 +351,7 @@ export class Responder {
|
|
|
348
351
|
async reconcile(entry) {
|
|
349
352
|
const state = await this.readState(entry);
|
|
350
353
|
if (state !== 'outcome_unknown') {
|
|
351
|
-
if (state !== null && (TERMINAL.has(state) || state
|
|
354
|
+
if (state !== null && (TERMINAL.has(state) || NOT_OURS.has(state))) this.checkpoint.drop(entry.request);
|
|
352
355
|
else if (state === 'in_progress' && entry.step !== 'unknown') {
|
|
353
356
|
entry.step = 'unknown'; // no next holder took the slot yet: a later tick tries again (finding 38)
|
|
354
357
|
this.checkpoint.put(entry);
|
|
@@ -39,7 +39,8 @@ export const REFUSAL_CODES = Object.freeze(new Set(['invalid_arguments', 'body_t
|
|
|
39
39
|
'capacity_exceeded', 'digest_conflict', 'revision_conflict', 'fenced', 'lease_expired', 'invalid_transition', 'reconcile_required',
|
|
40
40
|
'observation_required', 'cursor_reset_required', 'immutable_participants', 'held']));
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
// answered (DEC-0098, DEC-0102): the target Project's participant message answered it; never a responder's fenced reply.
|
|
43
|
+
export const REQUEST_STATES = ['queued', 'claimed', 'accepted', 'in_progress', 'completed', 'failed_known', 'cancelled', 'expired', 'outcome_unknown', 'answered'];
|
|
43
44
|
|
|
44
45
|
const EFFECT_STATES = ['not_started', 'started', 'succeeded_observed', 'failed_observed', 'unknown'];
|
|
45
46
|
|
|
@@ -139,7 +140,7 @@ function attemptProblems(a, at, problems) {
|
|
|
139
140
|
/** comms-receipt at the pin, closed at every level; transferred answers com.responder_replace only, each request once. */
|
|
140
141
|
export function receiptProblems(value, op) {
|
|
141
142
|
const problems = [];
|
|
142
|
-
const allowed = ['ok', 'repeated', 'message', 'thread', 'at', 'request', 'slot', 'attempt', 'transferred'];
|
|
143
|
+
const allowed = ['ok', 'repeated', 'message', 'thread', 'at', 'request', 'slot', 'attempt', 'transferred', 'marked', 'alreadyRead', 'more', 'noResponder'];
|
|
143
144
|
if (!closed(value, '', ['ok', 'repeated'], allowed, problems)) return problems;
|
|
144
145
|
if (value.ok !== true) problems.push('/ok is true');
|
|
145
146
|
if (typeof value.repeated !== 'boolean') problems.push('/repeated is a boolean');
|
|
@@ -189,6 +190,21 @@ export function receiptProblems(value, op) {
|
|
|
189
190
|
});
|
|
190
191
|
}
|
|
191
192
|
}
|
|
193
|
+
// DEC-0098: a bulk com.read_ack counts what it marked; `more` is present only as true (more than 1000 qualified).
|
|
194
|
+
for (const k of ['marked', 'alreadyRead']) if (k in value && !isInt(value[k], 0)) problems.push(`/${k} is an integer ≥ 0`);
|
|
195
|
+
if ('more' in value && value.more !== true) problems.push('/more is true when present');
|
|
196
|
+
if (op !== undefined && ['marked', 'alreadyRead', 'more'].some((k) => k in value) && op !== 'com.read_ack') problems.push('/marked, /alreadyRead and /more answer com.read_ack only (DEC-0098)');
|
|
197
|
+
if ('noResponder' in value) {
|
|
198
|
+
// DEC-0098: only in the requester's own com.submit receipt for a request; the request is stored all the same.
|
|
199
|
+
const n = value.noResponder;
|
|
200
|
+
if (op !== undefined && op !== 'com.submit') problems.push('/noResponder answers com.submit only (DEC-0098)');
|
|
201
|
+
const keys = ['target', 'capability', 'message'];
|
|
202
|
+
if (closed(n, '/noResponder', keys, keys, problems)) {
|
|
203
|
+
if (!str(n.target, PROJECT)) problems.push('/noResponder/target is a Project id');
|
|
204
|
+
if (!str(n.capability, CAPABILITY)) problems.push('/noResponder/capability is a capability name');
|
|
205
|
+
if (!(typeof n.message === 'string' && [...n.message].length >= 1 && [...n.message].length <= 300)) problems.push('/noResponder/message is 1 to 300 characters');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
192
208
|
return problems;
|
|
193
209
|
}
|
|
194
210
|
|
|
@@ -174,7 +174,7 @@ export function descriptorMemberProblems(d) {
|
|
|
174
174
|
const m = typeof peer.endpoint === 'string' && peer.endpoint.length <= 2048 ? PEER_ENDPOINT.exec(peer.endpoint) : null;
|
|
175
175
|
if (!m) problems.push('peer.endpoint is https://<dns-name>[:<port>][/<path>], no query, fragment, userinfo or IP literal');
|
|
176
176
|
else if (RESERVED_HOST.test(m[1])) problems.push(`FAC-SEM-085: a service offered to others cannot be reached at the reserved name ${m[1]}`);
|
|
177
|
-
if (!['organization-managed', 'personal'].includes(peer.custody)) problems.push('peer.custody is organization-managed or
|
|
177
|
+
if (!['organization-managed', 'personal', 'processor-hosted'].includes(peer.custody)) problems.push('peer.custody is organization-managed, personal or processor-hosted'); // processor-hosted: DEC-0099
|
|
178
178
|
const r = peer.residency;
|
|
179
179
|
if (!Array.isArray(r) || r.length < 1 || r.length > 32 || new Set(r.map((x) => JSON.stringify(x))).size !== r.length || !r.every((x) => typeof x === 'string' && RESIDENCY.test(x))) {
|
|
180
180
|
problems.push('peer.residency is 1 to 32 distinct ISO 3166-1 alpha-2 codes in upper case, EU admitted, no user-assigned code');
|
|
@@ -77,6 +77,8 @@ export function checkUsage(usage, nulls = false) {
|
|
|
77
77
|
// fabric_interop.py. Every function here answers and never throws on malformed input.
|
|
78
78
|
|
|
79
79
|
export const QUOTA_EXHAUSTED = 'quota-exhausted';
|
|
80
|
+
/** DEC-0104: a refusal at a job's start; a job it ended is malformed, so a caller answers it `invalid`. */
|
|
81
|
+
export const QUEUE_FULL = 'queue-full';
|
|
80
82
|
export const OUTCOME_UNKNOWN = 'outcome-unknown';
|
|
81
83
|
export const LIMIT_REACHED = 'limit-reached';
|
|
82
84
|
export const INTEROP_NULL_USAGE = 'fabric-interop/0.2';
|
|
@@ -151,6 +153,7 @@ export function jobCallerAction(answer, now) {
|
|
|
151
153
|
const codeOk = (isString(code) && ERROR_CODE.test(code)) || Number.isInteger(code);
|
|
152
154
|
const shapeOk = isObject(error) && Object.keys(error).every((name) => name === 'code' || name === 'message' || name === 'data');
|
|
153
155
|
if (!shapeOk || !codeOk || !isString(message) || message.length === 0 || codePoints(message) > MAX_ERROR_MESSAGE) return { action: 'invalid' };
|
|
156
|
+
if (code === QUEUE_FULL) return { action: 'invalid' };
|
|
154
157
|
if (code === OUTCOME_UNKNOWN) return { action: 'reconcile' };
|
|
155
158
|
if (code === LIMIT_REACHED) return { action: 'person' };
|
|
156
159
|
if (code !== QUOTA_EXHAUSTED) return { action: 'failed' };
|
|
@@ -24,6 +24,7 @@ export {
|
|
|
24
24
|
childTraceparent,
|
|
25
25
|
traceIds,
|
|
26
26
|
QUOTA_EXHAUSTED,
|
|
27
|
+
QUEUE_FULL,
|
|
27
28
|
OUTCOME_UNKNOWN,
|
|
28
29
|
LIMIT_REACHED,
|
|
29
30
|
INTEROP_NULL_USAGE,
|
|
@@ -41,7 +42,7 @@ export {
|
|
|
41
42
|
pauseMessage,
|
|
42
43
|
repeatedKeyDecision,
|
|
43
44
|
} from './fabric-interop-rules.mjs';
|
|
44
|
-
import { PROTOCOL, EXTENSION_KEY, MCP_REVISION, TERMINAL, CONTRACT_VERSION, JOB_HANDLE_SCHEMA, InteropError, UnknownJob, parseTraceparent, childTraceparent, traceIds, QUOTA_EXHAUSTED, OUTCOME_UNKNOWN, LIMIT_REACHED, INTEROP_NULL_USAGE, PROTOCOLS_READ, utcTime, quotaDataOk, readJobPause, jobCallerAction, intentIdempotencyKey, readsNullUsage, requestMeta, usageFindings, usageToward, jobLimitRules, pauseMessage, repeatedKeyDecision, OUTCOMES, ENVELOPE_REQUIRED, JOB_ID, KEY, SECRET_WORDS } from './fabric-interop-rules.mjs'; // the rule layer (FAA-92)
|
|
45
|
+
import { PROTOCOL, EXTENSION_KEY, MCP_REVISION, TERMINAL, CONTRACT_VERSION, JOB_HANDLE_SCHEMA, InteropError, UnknownJob, parseTraceparent, childTraceparent, traceIds, QUOTA_EXHAUSTED, QUEUE_FULL, OUTCOME_UNKNOWN, LIMIT_REACHED, INTEROP_NULL_USAGE, PROTOCOLS_READ, utcTime, quotaDataOk, readJobPause, jobCallerAction, intentIdempotencyKey, readsNullUsage, requestMeta, usageFindings, usageToward, jobLimitRules, pauseMessage, repeatedKeyDecision, OUTCOMES, ENVELOPE_REQUIRED, JOB_ID, KEY, SECRET_WORDS } from './fabric-interop-rules.mjs'; // the rule layer (FAA-92)
|
|
45
46
|
|
|
46
47
|
// --- C3.1 capabilities as tools ----------------------------------------------------------
|
|
47
48
|
export function expectedAnnotations(effect, idempotency) {
|
|
@@ -256,7 +257,11 @@ export class JobStore {
|
|
|
256
257
|
return this.transition(jobId, 'completed', { result });
|
|
257
258
|
}
|
|
258
259
|
|
|
259
|
-
fail(jobId, code, message, data) {
|
|
260
|
+
fail(jobId, code, message, data) {
|
|
261
|
+
// DEC-0104: a refusal at a job's start, never a job's end (the schema refuses one).
|
|
262
|
+
if (code === QUEUE_FULL) throw new InteropError('queue-full refuses a start and never ends a job (DEC-0104)');
|
|
263
|
+
return this.transition(jobId, 'failed', { error: data === undefined || data === null ? { code, message } : { code, message, data } });
|
|
264
|
+
}
|
|
260
265
|
|
|
261
266
|
// A job a limit paused and its provider will resume: working, the pause grammar, pollIntervalMs reaching the reset.
|
|
262
267
|
pause(jobId, { resetsAt, text } = {}) {
|
|
@@ -7,12 +7,13 @@ import { credentialName, secretArgument, secretUrl, secretValue } from './fabric
|
|
|
7
7
|
|
|
8
8
|
/** Each contract protocol's published revisions, oldest first (the contract's `PROTOCOL_REVISIONS`); a test holds it equal to the contract's. */
|
|
9
9
|
export const PROTOCOL_REVISIONS = new Map(Object.entries({
|
|
10
|
-
'access-log': ['1'], approval: ['1'], 'activity-summary': ['1'], 'coding-agents': ['1'], 'enroll-token': ['1'], entitlement: ['1'], 'entitlement-key-set': ['1'], 'estate-record': ['1'], 'execution-packet': ['1'],
|
|
11
|
-
'fabric-activity': ['0.1'], 'fabric-connections': ['0.1'], 'fabric-dashboard': ['0.1'], 'fabric-delegation': ['0.1'], 'fabric-device': ['0.1'],
|
|
10
|
+
'access-log': ['1'], approval: ['1'], 'activity-summary': ['1'], 'agent-directory': ['1'], 'coding-agents': ['1'], 'enroll-token': ['1'], entitlement: ['1'], 'entitlement-key-set': ['1'], 'estate-record': ['1'], 'execution-packet': ['1'],
|
|
11
|
+
'fabric-activity': ['0.1'], 'fabric-connections': ['0.1'], 'fabric-load': ['0.1'], 'fabric-dashboard': ['0.1'], 'fabric-delegation': ['0.1'], 'fabric-device': ['0.1'],
|
|
12
12
|
'fabric-interop': ['0.1', '0.2'], 'fabric-local-channel': ['0.1'], 'fabric-local-runner': ['0.1'], 'fabric-operator-channel': ['0.1'],
|
|
13
13
|
'fabric-package': ['1'], 'fabric-project-comms': ['0.1'], 'fabric-proposal': ['1'], 'fabric-provider': ['0.1'], 'fabric-relay': ['0.1'],
|
|
14
14
|
'fabric-secret-envelope': ['1'], 'fabric-service': ['0.1'], 'fabric-settings-backup': ['1'], heartbeat: ['1'], 'learning-note': ['1'],
|
|
15
|
-
memory: ['0.1'], 'pairing-message': ['1'], pipeline: ['0.1'], '
|
|
15
|
+
memory: ['0.1'], 'node-summary': ['1'], 'pairing-message': ['1'], pipeline: ['0.1'], 'repair-request': ['1'], 'repair-result': ['1'],
|
|
16
|
+
'revocation-delta': ['1'], 'session-handoff': ['1'], 'service-load': ['1'],
|
|
16
17
|
'share-registration': ['1'], 'share-registration-receipt': ['1'], 'support-quality-daily': ['1'], 'window-lease': ['1'],
|
|
17
18
|
}));
|
|
18
19
|
|
|
@@ -61,6 +61,9 @@ STOP_CODES = frozenset(["fenced", "lease_expired", "authority_changed"])
|
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
TERMINAL = frozenset(["completed", "failed_known", "cancelled", "expired"])
|
|
64
|
+
#: No longer this responder's to work on: back in the queue, claimed again, or answered by the target Project's
|
|
65
|
+
#: participant reply (DEC-0098, DEC-0102; not terminal, the requester may still cancel it, and nobody claims it).
|
|
66
|
+
NOT_OURS = frozenset(["queued", "claimed", "answered"])
|
|
64
67
|
|
|
65
68
|
|
|
66
69
|
#: A lost call is sent again this long before the lease it keeps would lapse (FAA-77, finding 38).
|
|
@@ -283,8 +286,8 @@ class Responder:
|
|
|
283
286
|
self.checkpoint.drop(entry["request"])
|
|
284
287
|
continue
|
|
285
288
|
raise
|
|
286
|
-
if state is None or state in TERMINAL or state in
|
|
287
|
-
self.checkpoint.drop(entry["request"]) # settled, or
|
|
289
|
+
if state is None or state in TERMINAL or state in NOT_OURS:
|
|
290
|
+
self.checkpoint.drop(entry["request"]) # settled, or no longer this responder's (NOT_OURS)
|
|
288
291
|
elif state == "accepted":
|
|
289
292
|
transfer.setdefault(entry["capability"], []).append(entry)
|
|
290
293
|
else: # in_progress (lost result once a new generation holds the slot) or outcome_unknown
|
|
@@ -401,7 +404,7 @@ class Responder:
|
|
|
401
404
|
def _reconcile(self, entry: Dict[str, Any]) -> None:
|
|
402
405
|
state = self._read_state(entry)
|
|
403
406
|
if state != "outcome_unknown":
|
|
404
|
-
if state in TERMINAL or state in
|
|
407
|
+
if state in TERMINAL or state in NOT_OURS:
|
|
405
408
|
self.checkpoint.drop(entry["request"])
|
|
406
409
|
elif state == "in_progress" and entry.get("step") != "unknown":
|
|
407
410
|
entry["step"] = "unknown" # no next holder took the slot yet: a later tick tries again (finding 38)
|
|
@@ -46,8 +46,9 @@ REFUSAL_CODES = frozenset(["invalid_arguments", "body_too_large", "not_authorize
|
|
|
46
46
|
"cursor_reset_required", "immutable_participants", "held"])
|
|
47
47
|
|
|
48
48
|
|
|
49
|
+
#: answered (DEC-0098, DEC-0102): the target Project's participant message answered it; never a responder's fenced reply.
|
|
49
50
|
REQUEST_STATES = ("queued", "claimed", "accepted", "in_progress", "completed", "failed_known", "cancelled", "expired",
|
|
50
|
-
"outcome_unknown")
|
|
51
|
+
"outcome_unknown", "answered")
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
EFFECT_STATES = ("not_started", "started", "succeeded_observed", "failed_observed", "unknown")
|
|
@@ -176,7 +177,8 @@ def receipt_problems(value: Any, op: Optional[str] = None) -> List[str]:
|
|
|
176
177
|
"""`comms-receipt.schema.json` at the pin, closed at every level (a seq or a holder is refused, C3), plus what a
|
|
177
178
|
schema cannot say: `transferred` answers only `com.responder_replace` (DEC-0078) and names each request once."""
|
|
178
179
|
problems: List[str] = []
|
|
179
|
-
allowed = ("ok", "repeated", "message", "thread", "at", "request", "slot", "attempt", "transferred"
|
|
180
|
+
allowed = ("ok", "repeated", "message", "thread", "at", "request", "slot", "attempt", "transferred", "marked",
|
|
181
|
+
"alreadyRead", "more", "noResponder")
|
|
180
182
|
if not _closed(value, "", ("ok", "repeated"), allowed, problems):
|
|
181
183
|
return problems
|
|
182
184
|
if value.get("ok") is not True:
|
|
@@ -241,6 +243,27 @@ def receipt_problems(value: Any, op: Optional[str] = None) -> List[str]:
|
|
|
241
243
|
if not _is_int(item.get("revision"), 1):
|
|
242
244
|
problems.append("%s/revision is an integer ≥ 1" % at)
|
|
243
245
|
_attempt_problems(item.get("attempt"), at + "/attempt", problems)
|
|
246
|
+
# DEC-0098: a bulk com.read_ack counts what it marked; `more` is present only as true (more than 1000 qualified).
|
|
247
|
+
for key in ("marked", "alreadyRead"):
|
|
248
|
+
if key in value and not _is_int(value[key], 0):
|
|
249
|
+
problems.append("/%s is an integer ≥ 0" % key)
|
|
250
|
+
if "more" in value and value["more"] is not True:
|
|
251
|
+
problems.append("/more is true when present")
|
|
252
|
+
if op is not None and any(k in value for k in ("marked", "alreadyRead", "more")) and op != "com.read_ack":
|
|
253
|
+
problems.append("/marked, /alreadyRead and /more answer com.read_ack only (DEC-0098)")
|
|
254
|
+
if "noResponder" in value:
|
|
255
|
+
# DEC-0098: only in the requester's own com.submit receipt for a request; the request is stored all the same.
|
|
256
|
+
n = value["noResponder"]
|
|
257
|
+
if op is not None and op != "com.submit":
|
|
258
|
+
problems.append("/noResponder answers com.submit only (DEC-0098)")
|
|
259
|
+
keys = ("target", "capability", "message")
|
|
260
|
+
if _closed(n, "/noResponder", keys, keys, problems):
|
|
261
|
+
if not (isinstance(n.get("target"), str) and _PROJECT.fullmatch(n["target"])):
|
|
262
|
+
problems.append("/noResponder/target is a Project id")
|
|
263
|
+
if not (isinstance(n.get("capability"), str) and _CAPABILITY.fullmatch(n["capability"])):
|
|
264
|
+
problems.append("/noResponder/capability is a capability name")
|
|
265
|
+
if not (isinstance(n.get("message"), str) and 1 <= len(n["message"]) <= 300):
|
|
266
|
+
problems.append("/noResponder/message is 1 to 300 characters")
|
|
244
267
|
return problems
|
|
245
268
|
|
|
246
269
|
|
|
@@ -285,8 +285,8 @@ def descriptor_member_problems(descriptor: Dict[str, Any]) -> List[str]:
|
|
|
285
285
|
problems.append("peer.endpoint is https://<dns-name>[:<port>][/<path>], no query, fragment, userinfo or IP literal")
|
|
286
286
|
elif _RESERVED_HOST.search(match.group("host")):
|
|
287
287
|
problems.append("FAC-SEM-085: a service offered to others cannot be reached at the reserved name %s" % match.group("host"))
|
|
288
|
-
if peer.get("custody") not in ("organization-managed", "personal"):
|
|
289
|
-
problems.append("peer.custody is organization-managed or
|
|
288
|
+
if peer.get("custody") not in ("organization-managed", "personal", "processor-hosted"): # processor-hosted: DEC-0099
|
|
289
|
+
problems.append("peer.custody is organization-managed, personal or processor-hosted")
|
|
290
290
|
residency = peer.get("residency")
|
|
291
291
|
if (not isinstance(residency, list) or not 1 <= len(residency) <= 32 or len(set(map(json.dumps, residency))) != len(residency)
|
|
292
292
|
or not all(isinstance(r, str) and _RESIDENCY.match(r) for r in residency)):
|
|
@@ -54,6 +54,7 @@ from fabric_interop_rules import ( # noqa: E402,F401 - the rule layer (FAA-92)
|
|
|
54
54
|
trace_ids,
|
|
55
55
|
_usage,
|
|
56
56
|
QUOTA_EXHAUSTED,
|
|
57
|
+
QUEUE_FULL,
|
|
57
58
|
OUTCOME_UNKNOWN,
|
|
58
59
|
LIMIT_REACHED,
|
|
59
60
|
INTEROP_NULL_USAGE,
|
|
@@ -392,6 +393,8 @@ class JobStore:
|
|
|
392
393
|
return self._transition(job_id, "completed", result=envelope)
|
|
393
394
|
|
|
394
395
|
def fail(self, job_id: str, code: Any, message: str, data: Any = None) -> Dict[str, Any]:
|
|
396
|
+
if code == QUEUE_FULL: # DEC-0104: a refusal at a job's start, never a job's end (the schema refuses one)
|
|
397
|
+
raise InteropError("queue-full refuses a start and never ends a job (DEC-0104)")
|
|
395
398
|
error: Dict[str, Any] = {"code": code, "message": message}
|
|
396
399
|
if data is not None:
|
|
397
400
|
error["data"] = data
|
|
@@ -121,6 +121,8 @@ def _usage(usage: Any, nulls: bool = False) -> Dict[str, Any]:
|
|
|
121
121
|
# here answers and never raises on malformed input.
|
|
122
122
|
|
|
123
123
|
QUOTA_EXHAUSTED = "quota-exhausted"
|
|
124
|
+
#: DEC-0104: a refusal at a job's start; a job it ended is malformed, so a caller answers it `invalid`.
|
|
125
|
+
QUEUE_FULL = "queue-full"
|
|
124
126
|
OUTCOME_UNKNOWN = "outcome-unknown"
|
|
125
127
|
LIMIT_REACHED = "limit-reached"
|
|
126
128
|
INTEROP_NULL_USAGE = "fabric-interop/0.2"
|
|
@@ -225,6 +227,8 @@ def job_caller_action(answer: Any, now: Any = _ABSENT) -> Dict[str, Any]:
|
|
|
225
227
|
shape_ok = isinstance(error, dict) and all(name in ("code", "message", "data") for name in error)
|
|
226
228
|
if not shape_ok or not code_ok or not isinstance(message, str) or not message or _code_points(message) > _MAX_ERROR_MESSAGE:
|
|
227
229
|
return {"action": "invalid"}
|
|
230
|
+
if code == QUEUE_FULL:
|
|
231
|
+
return {"action": "invalid"}
|
|
228
232
|
if code == OUTCOME_UNKNOWN:
|
|
229
233
|
return {"action": "reconcile"}
|
|
230
234
|
if code == LIMIT_REACHED:
|
|
@@ -13,14 +13,14 @@ if str(_Path(__file__).resolve().parent) not in _sys.path: # the kit loads from
|
|
|
13
13
|
#: Each contract protocol's published revisions, oldest first (the contract's `PROTOCOL_REVISIONS`, DEC-0067); a test
|
|
14
14
|
#: holds it equal to the contract's register at the pin.
|
|
15
15
|
PROTOCOL_REVISIONS: Dict[str, Tuple[str, ...]] = {
|
|
16
|
-
"access-log": ("1",), "approval": ("1",), "activity-summary": ("1",), "coding-agents": ("1",), "enroll-token": ("1",), "entitlement": ("1",),
|
|
16
|
+
"access-log": ("1",), "approval": ("1",), "activity-summary": ("1",), "agent-directory": ("1",), "coding-agents": ("1",), "enroll-token": ("1",), "entitlement": ("1",),
|
|
17
17
|
"entitlement-key-set": ("1",), "estate-record": ("1",), "execution-packet": ("1",),
|
|
18
|
-
"fabric-activity": ("0.1",), "fabric-connections": ("0.1",), "fabric-dashboard": ("0.1",), "fabric-delegation": ("0.1",), "fabric-device": ("0.1",),
|
|
18
|
+
"fabric-activity": ("0.1",), "fabric-connections": ("0.1",), "fabric-load": ("0.1",), "fabric-dashboard": ("0.1",), "fabric-delegation": ("0.1",), "fabric-device": ("0.1",),
|
|
19
19
|
"fabric-interop": ("0.1", "0.2"), "fabric-local-channel": ("0.1",), "fabric-local-runner": ("0.1",),
|
|
20
20
|
"fabric-operator-channel": ("0.1",), "fabric-package": ("1",), "fabric-project-comms": ("0.1",), "fabric-proposal": ("1",),
|
|
21
21
|
"fabric-provider": ("0.1",), "fabric-relay": ("0.1",), "fabric-secret-envelope": ("1",), "fabric-service": ("0.1",),
|
|
22
|
-
"fabric-settings-backup": ("1",), "heartbeat": ("1",), "learning-note": ("1",), "memory": ("0.1",), "
|
|
23
|
-
"revocation-delta": ("1",), "session-handoff": ("1",), "share-registration": ("1",),
|
|
22
|
+
"fabric-settings-backup": ("1",), "heartbeat": ("1",), "learning-note": ("1",), "memory": ("0.1",), "node-summary": ("1",), "pairing-message": ("1",),
|
|
23
|
+
"pipeline": ("0.1",), "repair-request": ("1",), "repair-result": ("1",), "revocation-delta": ("1",), "session-handoff": ("1",), "service-load": ("1",), "share-registration": ("1",),
|
|
24
24
|
"share-registration-receipt": ("1",), "support-quality-daily": ("1",), "window-lease": ("1",),
|
|
25
25
|
}
|
|
26
26
|
|