borgmcp-shared 0.10.0 → 0.11.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.
- package/CONTRIBUTING.md +2 -2
- package/README.md +17 -17
- package/RELEASES.md +5 -0
- package/dist/conformance/adapter.d.ts +1 -5
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +1 -26
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/protocol/contract.d.ts +1 -2
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +1 -2
- package/dist/protocol/contract.js.map +1 -1
- package/dist/protocol/errors.d.ts +0 -1
- package/dist/protocol/errors.d.ts.map +1 -1
- package/dist/protocol/errors.js +0 -1
- package/dist/protocol/errors.js.map +1 -1
- package/dist/templates.d.ts +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +178 -53
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +1 -1
- package/docs/release-records.json +28 -0
- package/docs/releasing.md +2 -3
- package/docs/template-lifecycle.md +66 -0
- package/package.json +2 -2
- package/src/conformance/adapter.ts +2 -38
- package/src/protocol/contract.ts +1 -2
- package/src/protocol/errors.ts +1 -3
- package/src/templates.ts +185 -53
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Built-in template lifecycle
|
|
2
|
+
|
|
3
|
+
Built-in templates are versioned package data. `src/templates.ts` is the source of truth for their names, presentation copy, cube directives, message taxonomies, roles, flags, short descriptions, and detailed playbooks. A running cube is not a writable upstream for that source.
|
|
4
|
+
|
|
5
|
+
## Supported lifecycle
|
|
6
|
+
|
|
7
|
+
### Edit one cube locally
|
|
8
|
+
|
|
9
|
+
A client with live manage authority for the cube may use the role and cube mutation operations to create or update roles, patch one plain-label role section, or update the cube directive or message taxonomy. Prefer a one-section role patch when one section changes; it preserves every other byte. Local edits affect only that cube. They neither mutate `src/templates.ts` nor create a built-in variant.
|
|
10
|
+
|
|
11
|
+
Read the canonical role or cube state before editing and read it back after the mutation. The server's canonical state is the result to verify. The package does not provide an automatic local-change-to-template audit ledger or promotion path.
|
|
12
|
+
|
|
13
|
+
### Promote a trusted local improvement
|
|
14
|
+
|
|
15
|
+
Promotion is a normal reviewed repository change, not a runtime copy operation:
|
|
16
|
+
|
|
17
|
+
1. Compare the local text with the current built-in source and identify the behavior and failure it addresses.
|
|
18
|
+
2. Generalize only what applies to new cubes across supported implementations and hosts. Remove repository-host, operator-machine, project, and live-cube details.
|
|
19
|
+
3. Edit `src/templates.ts` and the focused existing tests that pin the affected invariant. Preserve plain-label sections and the role-section round-trip contract.
|
|
20
|
+
4. Review the complete source change through the repository's protected change mechanism. The change history and review are the built-in template's durable audit record.
|
|
21
|
+
5. Publish the reviewed package through the normal immutable shared-package release workflow.
|
|
22
|
+
6. Update client and server to the same exact `borgmcp-shared` version as a matched pair. The client update path rejects a published pair whose exact shared pins differ.
|
|
23
|
+
|
|
24
|
+
There is no direct promotion from server state, no package write from a running cube, and no unreviewed operator override of a built-in template.
|
|
25
|
+
|
|
26
|
+
### Apply a released template
|
|
27
|
+
|
|
28
|
+
New cubes created through an updated client/server pair receive the template data shipped by that exact shared package. An existing non-empty cube remains locally owned: template application does not overwrite its directive (`resolveCubeDirectiveForApply` returns `null`). A release does not rewrite existing roles, directives, or taxonomies. An operator who wants a released improvement in an existing cube applies that specific local edit deliberately and verifies canonical readback.
|
|
29
|
+
|
|
30
|
+
## Authority and audit
|
|
31
|
+
|
|
32
|
+
Cube-local authority and package authority are separate:
|
|
33
|
+
|
|
34
|
+
- A cube-local edit requires the cube's live manage authority and is verified against canonical server readback. It is not evidence that a built-in changed.
|
|
35
|
+
- A built-in edit requires repository change authority, review of the source diff, and the package release gates. Git history, review, immutable tag, and package artifact are its audit chain.
|
|
36
|
+
- A shared release alone is not delivery to users. Client and server must both pin the same exact shared version; neither a range nor one-sided pin movement is supported.
|
|
37
|
+
- Merge, tag, publication, and consumer pin changes remain separate authorized actions. Editing a local cube grants none of them.
|
|
38
|
+
|
|
39
|
+
## Compatibility, versions, and migration
|
|
40
|
+
|
|
41
|
+
Built-in templates have no independent runtime version or negotiation protocol. Their version is the `borgmcp-shared` package version that contains them.
|
|
42
|
+
|
|
43
|
+
A role-text, directive, or taxonomy change ships in a new shared package version. Client and server consume the same exact version. If a change also alters a wire-visible template name or closed protocol acceptance set, follow the protocol change policy: update the exact protocol tag and implementation-neutral conformance vectors, then coordinate both consumers. Text-only playbook improvements do not invent a protocol migration.
|
|
44
|
+
|
|
45
|
+
Existing cubes are snapshots with local ownership, not replicas. They do not auto-migrate or auto-synchronize. This no-clobber rule avoids erasing operator edits and avoids making package upgrades mutate active coordination policy. Migration, when desired, is an explicit cube-local edit of named fields or sections.
|
|
46
|
+
|
|
47
|
+
## Validation and conformance
|
|
48
|
+
|
|
49
|
+
Use the existing focused controls; do not add a second template lifecycle mechanism:
|
|
50
|
+
|
|
51
|
+
- `test/templates.test.ts` pins the built-in set, expected software-development roles and flags, host-neutral presentation copy, authority/scope language, role-size budgets, Builder minimum-sufficient-change ordering, taxonomy routing, generated/source equality, and no-clobber resolution.
|
|
52
|
+
- `test/role-section.test.ts` pins plain-label parsing, byte-identical parse/serialize for real template roles, and single-section replacement/insertion/deletion without clobbering unrelated text.
|
|
53
|
+
- `test/packed-artifact.test.ts` installs the packed package and verifies the public named-template creation contract and shipped role text from the consumer surface.
|
|
54
|
+
- `CUBE_TEMPLATE_ACCEPTANCE_CONFORMANCE` pins the closed accepted template-name set and invalid-name rejection. Stateful create vectors pin template identity to retry behavior.
|
|
55
|
+
|
|
56
|
+
Review remains the host-neutrality control: role text must avoid a specific repository host, CI vendor, operating system adapter, local path, or project-only workflow unless the template's documented scope requires it. This is a semantic review question, not a keyword validator. Validate both source and the built/packed consumer artifact because built-ins are delivered through the package.
|
|
57
|
+
|
|
58
|
+
## Rejected alternatives
|
|
59
|
+
|
|
60
|
+
- Runtime-editable built-ins: rejected because package source and running server state would become competing authorities without one review or release history.
|
|
61
|
+
- Automatic promotion from a local cube: rejected because live text may contain project-specific policy, host assumptions, temporary experiments, or unreviewed mistakes.
|
|
62
|
+
- Automatic synchronization into existing cubes: rejected because it would clobber local policy and role edits. Existing cubes remain unchanged unless an operator applies a named edit.
|
|
63
|
+
- Separate template versions or compatibility negotiation: rejected because package identity and exact matched consumer pins already provide the delivery boundary; another version axis would add ambiguity without preserving any supported behavior.
|
|
64
|
+
- A new lifecycle validator or audit service: rejected because current behavioral tests, role-section round trips, protected review, and artifact gates already cover the enforceable invariants. Host-neutrality and promotion judgment remain review responsibilities.
|
|
65
|
+
|
|
66
|
+
Boundary: this document describes current mechanisms and the supported promotion path; it does not itself authorize a release, a local cube mutation, or a consumer pin change.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "borgmcp-shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Shared protocol, domain, templates, and conformance contracts for Borg MCP implementations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"verify:sbom": "node scripts/verify-release-sbom.mjs",
|
|
65
65
|
"release:prepare": "node scripts/release-identity.mjs prepare",
|
|
66
66
|
"verify:release-identity": "node scripts/release-identity.mjs verify",
|
|
67
|
-
"prepack": "npm run
|
|
67
|
+
"prepack": "npm run build && npm run check && npm test"
|
|
68
68
|
},
|
|
69
69
|
"keywords": [
|
|
70
70
|
"borgmcp",
|
|
@@ -70,7 +70,7 @@ export interface ConformanceDroneRuntimeState {
|
|
|
70
70
|
readonly metadata_revision: number;
|
|
71
71
|
readonly cube_id: string;
|
|
72
72
|
readonly role_id: string;
|
|
73
|
-
readonly session_state: 'active' | 'revoked'
|
|
73
|
+
readonly session_state: 'active' | 'revoked';
|
|
74
74
|
readonly evicted: boolean;
|
|
75
75
|
readonly last_seen: string;
|
|
76
76
|
readonly heartbeat_count: number;
|
|
@@ -215,7 +215,6 @@ export interface ConformanceAdmin {
|
|
|
215
215
|
): Promise<ConformanceDrone>;
|
|
216
216
|
issueManagedDroneSession(drone: ConformanceDrone): Promise<string>;
|
|
217
217
|
revokeManagedDroneSession(drone: ConformanceDrone): Promise<void>;
|
|
218
|
-
expireManagedDroneSession(drone: ConformanceDrone): Promise<void>;
|
|
219
218
|
inspectManagedDrone(drone: ConformanceDrone): Promise<{
|
|
220
219
|
readonly role_id: string;
|
|
221
220
|
readonly evicted: boolean;
|
|
@@ -375,7 +374,6 @@ export const ADAPTER_CONFORMANCE_FIXTURES = [
|
|
|
375
374
|
{ id: 'drones.evict-terminal-signal', area: 'drones' },
|
|
376
375
|
{ id: 'roles.delete-contract', area: 'roles' },
|
|
377
376
|
{ id: 'roles.rationale-contract', area: 'roles' },
|
|
378
|
-
{ id: 'security.drone-session-rejection-causes', area: 'security' },
|
|
379
377
|
{ id: 'metadata.attach-report', area: 'metadata' },
|
|
380
378
|
{ id: 'metadata.self-heal-patch', area: 'metadata' },
|
|
381
379
|
{ id: 'security.metadata-invalid-atomic', area: 'security' },
|
|
@@ -2387,36 +2385,6 @@ export async function runAdapterConformance(
|
|
|
2387
2385
|
};
|
|
2388
2386
|
});
|
|
2389
2387
|
|
|
2390
|
-
await record('security.drone-session-rejection-causes', async () => {
|
|
2391
|
-
const revokedDrone = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
2392
|
-
const expiredDrone = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
2393
|
-
const revokedCredential = await environment.admin.issueManagedDroneSession(revokedDrone);
|
|
2394
|
-
const expiredCredential = await environment.admin.issueManagedDroneSession(expiredDrone);
|
|
2395
|
-
await environment.admin.revokeManagedDroneSession(revokedDrone);
|
|
2396
|
-
await environment.admin.expireManagedDroneSession(expiredDrone);
|
|
2397
|
-
for (const [label, credential, code] of [
|
|
2398
|
-
['revoked', revokedCredential, ErrorCode.SESSION_REVOKED],
|
|
2399
|
-
['expired', expiredCredential, ErrorCode.AUTH_EXPIRED],
|
|
2400
|
-
] as const) {
|
|
2401
|
-
expectError(
|
|
2402
|
-
await environment.operations.read(
|
|
2403
|
-
credential,
|
|
2404
|
-
cubeA,
|
|
2405
|
-
createProtocolEnvelope(`${label}-seat-probe`, { cursor: null, limit: 1 }),
|
|
2406
|
-
),
|
|
2407
|
-
401,
|
|
2408
|
-
code,
|
|
2409
|
-
`${label} seat probe`,
|
|
2410
|
-
);
|
|
2411
|
-
}
|
|
2412
|
-
return {
|
|
2413
|
-
revoked_status: 401,
|
|
2414
|
-
expired_status: 401,
|
|
2415
|
-
revoked_code: ErrorCode.SESSION_REVOKED,
|
|
2416
|
-
expired_code: ErrorCode.AUTH_EXPIRED,
|
|
2417
|
-
};
|
|
2418
|
-
});
|
|
2419
|
-
|
|
2420
2388
|
const knownMetadata = {
|
|
2421
2389
|
agent_kind: 'opencode' as const,
|
|
2422
2390
|
reported_model: 'openai/gpt-5.6-sol',
|
|
@@ -2637,10 +2605,6 @@ export async function runAdapterConformance(
|
|
|
2637
2605
|
const revokedSession = await environment.admin.issueManagedDroneSession(revoked);
|
|
2638
2606
|
await environment.admin.revokeManagedDroneSession(revoked);
|
|
2639
2607
|
rejectedStates.push(['revoked', revoked, revokedSession, 401, ErrorCode.SESSION_REVOKED]);
|
|
2640
|
-
const expired = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
2641
|
-
const expiredSession = await environment.admin.issueManagedDroneSession(expired);
|
|
2642
|
-
await environment.admin.expireManagedDroneSession(expired);
|
|
2643
|
-
rejectedStates.push(['expired', expired, expiredSession, 401, ErrorCode.AUTH_EXPIRED]);
|
|
2644
2608
|
const evicted = await environment.admin.createDrone(principalA, cubeA, workerRoleA);
|
|
2645
2609
|
const evictedSession = await environment.admin.issueManagedDroneSession(evicted);
|
|
2646
2610
|
expectStatus(
|
|
@@ -2675,7 +2639,7 @@ export async function runAdapterConformance(
|
|
|
2675
2639
|
own_seat_only: true,
|
|
2676
2640
|
manager_denied: true,
|
|
2677
2641
|
unknown_session_denied: true,
|
|
2678
|
-
|
|
2642
|
+
revoked_evicted_denied: true,
|
|
2679
2643
|
};
|
|
2680
2644
|
});
|
|
2681
2645
|
|
package/src/protocol/contract.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
} from './types.js';
|
|
14
14
|
|
|
15
15
|
export const SHARED_PACKAGE_NAME = 'borgmcp-shared' as const;
|
|
16
|
-
export const SHARED_PACKAGE_VERSION = '0.
|
|
16
|
+
export const SHARED_PACKAGE_VERSION = '0.11.0' as const;
|
|
17
17
|
/** Maximum UTF-8 payload for each newly recorded decision text field. */
|
|
18
18
|
export const DECISION_TEXT_MAX_BYTES = 512 as const;
|
|
19
19
|
/** Maximum UTF-8 size of role detailed-description text and any returned section slice. */
|
|
@@ -92,7 +92,6 @@ export const PROTOCOL_HTTP_CONTRACT = {
|
|
|
92
92
|
},
|
|
93
93
|
auth_missing_status: 401,
|
|
94
94
|
auth_invalid_status: 401,
|
|
95
|
-
auth_expired_status: 401,
|
|
96
95
|
session_revoked_status: 401,
|
|
97
96
|
session_rejected_status: 401,
|
|
98
97
|
cursor_expired_status: 410,
|
package/src/protocol/errors.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
export enum ErrorCode {
|
|
3
3
|
AUTH_MISSING = 'AUTH_MISSING',
|
|
4
4
|
AUTH_INVALID = 'AUTH_INVALID',
|
|
5
|
-
AUTH_EXPIRED = 'AUTH_EXPIRED',
|
|
6
5
|
SUBSCRIPTION_REQUIRED = 'SUBSCRIPTION_REQUIRED',
|
|
7
6
|
ACCESS_DENIED = 'ACCESS_DENIED',
|
|
8
7
|
INVALID_INPUT = 'INVALID_INPUT',
|
|
@@ -33,8 +32,7 @@ export enum ErrorCode {
|
|
|
33
32
|
* The presented session bearer does not match the seat it targets: a fresh or
|
|
34
33
|
* non-matching bearer against an already-bound active seat. Distinct from
|
|
35
34
|
* SESSION_REVOKED (a formerly valid credential that was explicitly revoked).
|
|
36
|
-
*
|
|
37
|
-
* server's typed 401 takeover rejection.
|
|
35
|
+
* Carried by the server's typed 401 takeover rejection.
|
|
38
36
|
*/
|
|
39
37
|
SESSION_REJECTED = 'SESSION_REJECTED',
|
|
40
38
|
}
|