borgmcp-shared 0.14.0 → 1.0.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/README.md +4 -3
- package/RELEASES.md +7 -0
- package/dist/conformance/adapter.d.ts +9 -2
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +128 -43
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/conformance/index.d.ts +25 -3
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +73 -18
- package/dist/conformance/index.js.map +1 -1
- package/dist/protocol/contract.d.ts +1 -1
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +13 -18
- package/dist/protocol/contract.js.map +1 -1
- package/dist/protocol/coordination.d.ts +10 -0
- package/dist/protocol/coordination.d.ts.map +1 -1
- package/dist/protocol/coordination.js +20 -6
- package/dist/protocol/coordination.js.map +1 -1
- package/dist/protocol/errors.d.ts +1 -1
- package/dist/protocol/errors.d.ts.map +1 -1
- package/dist/protocol/errors.js +1 -1
- package/dist/protocol/errors.js.map +1 -1
- package/dist/protocol/types.d.ts +1 -5
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/templates.d.ts +1 -3
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +7 -79
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +9 -0
- package/docs/release-records.json +14 -0
- package/docs/releases/1.0.0.md +11 -0
- package/docs/template-lifecycle.md +1 -1
- package/package.json +1 -1
- package/src/conformance/adapter.ts +232 -52
- package/src/conformance/index.ts +88 -22
- package/src/protocol/contract.ts +15 -21
- package/src/protocol/coordination.ts +36 -7
- package/src/protocol/errors.ts +1 -1
- package/src/protocol/types.ts +1 -5
- package/src/protocol/version.ts +2 -2
- package/src/templates.ts +7 -81
package/src/conformance/index.ts
CHANGED
|
@@ -100,8 +100,6 @@ const APPEND_LOG_ENTRY: EnrichedStreamEntry = {
|
|
|
100
100
|
const APPEND_LOG_ROUTING = {
|
|
101
101
|
class: 'review',
|
|
102
102
|
recipients: ['one-of-one-reviewer'],
|
|
103
|
-
fellOpen: false,
|
|
104
|
-
message: 'Routing applied.',
|
|
105
103
|
} as const;
|
|
106
104
|
|
|
107
105
|
export interface AppendLogResultConformanceVector {
|
|
@@ -119,31 +117,71 @@ export interface AppendLogRequestConformanceVector {
|
|
|
119
117
|
export interface AppendLogIdempotencyConformanceVector {
|
|
120
118
|
name: string;
|
|
121
119
|
actor: 'same' | 'different';
|
|
122
|
-
mutation: 'none' | 'message' | '
|
|
120
|
+
mutation: 'none' | 'message' | 'audience' | 'recipient_set' | 'classification';
|
|
123
121
|
expected: 'deduplicated' | 'POST_ID_CONFLICT' | 'created';
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
/** Author-scoped post identity and exact resolved-
|
|
124
|
+
/** Author-scoped post identity and exact resolved-delivery retry outcomes. */
|
|
127
125
|
export const APPEND_LOG_IDEMPOTENCY_CONFORMANCE: readonly AppendLogIdempotencyConformanceVector[] = [
|
|
128
126
|
{ name: 'deduplicates an exact same-author retry', actor: 'same', mutation: 'none', expected: 'deduplicated' },
|
|
129
127
|
{ name: 'rejects a same-author message collision', actor: 'same', mutation: 'message', expected: 'POST_ID_CONFLICT' },
|
|
130
|
-
{ name: 'rejects a same-author
|
|
128
|
+
{ name: 'rejects a same-author audience collision', actor: 'same', mutation: 'audience', expected: 'POST_ID_CONFLICT' },
|
|
131
129
|
{ name: 'rejects a same-author recipient-set collision', actor: 'same', mutation: 'recipient_set', expected: 'POST_ID_CONFLICT' },
|
|
132
|
-
{ name: 'deduplicates
|
|
133
|
-
{ name: 'rejects a same-author resolved class-routing collision', actor: 'same', mutation: 'resolved_class_routing', expected: 'POST_ID_CONFLICT' },
|
|
130
|
+
{ name: 'deduplicates changed classification with identical explicit delivery', actor: 'same', mutation: 'classification', expected: 'deduplicated' },
|
|
134
131
|
{ name: 'creates an independent cross-author post', actor: 'different', mutation: 'none', expected: 'created' },
|
|
135
132
|
];
|
|
136
133
|
|
|
137
134
|
const APPEND_LOG_REQUEST = {
|
|
138
135
|
post_id: '00000000-0000-4000-8000-000000000205',
|
|
139
136
|
message: 'REVIEW-READY: example',
|
|
137
|
+
to: 'broadcast',
|
|
140
138
|
} satisfies AppendLogRequest;
|
|
141
139
|
|
|
142
140
|
export const APPEND_LOG_REQUEST_CONFORMANCE: readonly AppendLogRequestConformanceVector[] = [
|
|
143
141
|
{ name: 'accepts a canonical post UUID', request: APPEND_LOG_REQUEST, accepts: true },
|
|
144
|
-
{ name: 'rejects an omitted post UUID', request: { message: APPEND_LOG_REQUEST.message }, accepts: false },
|
|
142
|
+
{ name: 'rejects an omitted post UUID', request: { message: APPEND_LOG_REQUEST.message, to: 'broadcast' }, accepts: false },
|
|
145
143
|
{ name: 'rejects an invalid post UUID', request: { ...APPEND_LOG_REQUEST, post_id: 'not-a-uuid' }, accepts: false },
|
|
146
144
|
{ name: 'rejects unknown request fields', request: { ...APPEND_LOG_REQUEST, extra: true }, accepts: false },
|
|
145
|
+
{
|
|
146
|
+
name: 'rejects omitted addressing',
|
|
147
|
+
request: { post_id: APPEND_LOG_REQUEST.post_id, message: APPEND_LOG_REQUEST.message },
|
|
148
|
+
accepts: false,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
name: 'accepts explicit broadcast addressing',
|
|
152
|
+
request: APPEND_LOG_REQUEST,
|
|
153
|
+
accepts: true,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'accepts a non-empty recipient selector union branch',
|
|
157
|
+
request: { ...APPEND_LOG_REQUEST, to: ['Builder', 'id:3336cde1'] },
|
|
158
|
+
accepts: true,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'rejects null addressing',
|
|
162
|
+
request: { ...APPEND_LOG_REQUEST, to: null },
|
|
163
|
+
accepts: false,
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'rejects an empty recipient selector array',
|
|
167
|
+
request: { ...APPEND_LOG_REQUEST, to: [] },
|
|
168
|
+
accepts: false,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'rejects an arbitrary scalar audience',
|
|
172
|
+
request: { ...APPEND_LOG_REQUEST, to: 'all' },
|
|
173
|
+
accepts: false,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'rejects retired visibility without falling open',
|
|
177
|
+
request: { ...APPEND_LOG_REQUEST, visibility: 'broadcast' },
|
|
178
|
+
accepts: false,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'rejects retired recipient ids without falling open',
|
|
182
|
+
request: { ...APPEND_LOG_REQUEST, recipientDroneIds: ['00000000-0000-4000-8000-000000000204'] },
|
|
183
|
+
accepts: false,
|
|
184
|
+
},
|
|
147
185
|
];
|
|
148
186
|
|
|
149
187
|
/** Runtime response vectors keep the append-log decoder aligned with its public type. */
|
|
@@ -214,13 +252,8 @@ export const APPEND_LOG_RESULT_CONFORMANCE: readonly AppendLogResultConformanceV
|
|
|
214
252
|
accepts: false,
|
|
215
253
|
},
|
|
216
254
|
{
|
|
217
|
-
name: 'rejects
|
|
218
|
-
response: { entry: APPEND_LOG_ENTRY, deduplicated: false, routing: { ...APPEND_LOG_ROUTING, fellOpen:
|
|
219
|
-
accepts: false,
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
name: 'rejects an invalid routing message',
|
|
223
|
-
response: { entry: APPEND_LOG_ENTRY, deduplicated: false, routing: { ...APPEND_LOG_ROUTING, message: 7 } },
|
|
255
|
+
name: 'rejects legacy fall-open routing metadata',
|
|
256
|
+
response: { entry: APPEND_LOG_ENTRY, deduplicated: false, routing: { ...APPEND_LOG_ROUTING, fellOpen: false } },
|
|
224
257
|
accepts: false,
|
|
225
258
|
},
|
|
226
259
|
{
|
|
@@ -883,7 +916,6 @@ export interface RoleDeleteConformanceVector {
|
|
|
883
916
|
| 'default'
|
|
884
917
|
| 'mandatory'
|
|
885
918
|
| 'human-seat'
|
|
886
|
-
| 'taxonomy-reference'
|
|
887
919
|
| 'unknown';
|
|
888
920
|
expected:
|
|
889
921
|
| {
|
|
@@ -895,7 +927,7 @@ export interface RoleDeleteConformanceVector {
|
|
|
895
927
|
}
|
|
896
928
|
| {
|
|
897
929
|
status: 409;
|
|
898
|
-
error: 'ROLE_IN_USE' | 'DEFAULT_ROLE_REQUIRED' | 'ROLE_REQUIRED'
|
|
930
|
+
error: 'ROLE_IN_USE' | 'DEFAULT_ROLE_REQUIRED' | 'ROLE_REQUIRED';
|
|
899
931
|
mutation: 'none';
|
|
900
932
|
message?: typeof ROLE_IN_USE_DELETE_MESSAGE;
|
|
901
933
|
}
|
|
@@ -945,11 +977,6 @@ export const ROLE_DELETE_CONFORMANCE: readonly RoleDeleteConformanceVector[] = [
|
|
|
945
977
|
fixture: 'human-seat',
|
|
946
978
|
expected: { status: 409, error: 'ROLE_REQUIRED', mutation: 'none' },
|
|
947
979
|
},
|
|
948
|
-
{
|
|
949
|
-
name: 'refuses a taxonomy default-recipient role',
|
|
950
|
-
fixture: 'taxonomy-reference',
|
|
951
|
-
expected: { status: 409, error: 'ROLE_REFERENCED', mutation: 'none' },
|
|
952
|
-
},
|
|
953
980
|
{
|
|
954
981
|
name: 'hides an unknown or inaccessible role',
|
|
955
982
|
fixture: 'unknown',
|
|
@@ -1046,6 +1073,45 @@ export const ACK_STATUS_CONFORMANCE: readonly AckStatusConformanceVector[] = [
|
|
|
1046
1073
|
},
|
|
1047
1074
|
];
|
|
1048
1075
|
|
|
1076
|
+
export interface EntryQueryConformanceVector {
|
|
1077
|
+
name: string;
|
|
1078
|
+
fixture: 'full-uuid' | 'unique-prefix' | 'unknown' | 'ambiguous-prefix' | 'read-only';
|
|
1079
|
+
expected:
|
|
1080
|
+
| { status: 200; exact_entry: true; mutation: 'none' }
|
|
1081
|
+
| { status: 404; error: 'NOT_FOUND'; mutation: 'none' }
|
|
1082
|
+
| { status: 409; error: 'LOG_ENTRY_PREFIX_AMBIGUOUS'; mutation: 'none' }
|
|
1083
|
+
| { status: 200; unread_entry_available: true; mutation: 'none' };
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/** Portable exact-entry lookup outcomes for canonical UUID and short-prefix selectors. */
|
|
1087
|
+
export const ENTRY_QUERY_CONFORMANCE: readonly EntryQueryConformanceVector[] = [
|
|
1088
|
+
{
|
|
1089
|
+
name: 'resolves one canonical full UUID',
|
|
1090
|
+
fixture: 'full-uuid',
|
|
1091
|
+
expected: { status: 200, exact_entry: true, mutation: 'none' },
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
name: 'resolves one unique eight-hex prefix',
|
|
1095
|
+
fixture: 'unique-prefix',
|
|
1096
|
+
expected: { status: 200, exact_entry: true, mutation: 'none' },
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
name: 'hides an unknown or inaccessible selector',
|
|
1100
|
+
fixture: 'unknown',
|
|
1101
|
+
expected: { status: 404, error: 'NOT_FOUND', mutation: 'none' },
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
name: 'refuses an ambiguous eight-hex prefix',
|
|
1105
|
+
fixture: 'ambiguous-prefix',
|
|
1106
|
+
expected: { status: 409, error: 'LOG_ENTRY_PREFIX_AMBIGUOUS', mutation: 'none' },
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
name: 'does not advance unread or mutate acknowledgement, claim, log, or authority state',
|
|
1110
|
+
fixture: 'read-only',
|
|
1111
|
+
expected: { status: 200, unread_entry_available: true, mutation: 'none' },
|
|
1112
|
+
},
|
|
1113
|
+
];
|
|
1114
|
+
|
|
1049
1115
|
/** One canonical corpus consumed unchanged by shared, server, and client tests. */
|
|
1050
1116
|
export const RUNTIME_METADATA_REPOSITORY_CONFORMANCE:
|
|
1051
1117
|
readonly RuntimeMetadataRepositoryConformanceVector[] = [
|
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 = '1.0.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. */
|
|
@@ -590,7 +590,7 @@ export function decodeProtocolTagPreflight(value: unknown): ProtocolTagPreflight
|
|
|
590
590
|
exactKeys(input, ['protocol_version'], ['protocol_version']);
|
|
591
591
|
if (input.protocol_version !== PROTOCOL_VERSION) {
|
|
592
592
|
throw new ProtocolContractError(
|
|
593
|
-
'This client requires protocol
|
|
593
|
+
'This client requires protocol v12. The peer presents a different version. Update `borgmcp-server` and `borgmcp` to matching releases — server first, then client.',
|
|
594
594
|
ErrorCode.UNSUPPORTED_PROTOCOL_VERSION,
|
|
595
595
|
['protocol_version'],
|
|
596
596
|
);
|
|
@@ -1007,33 +1007,17 @@ export function decodeAppendLogRequest(value: unknown): import('./types.js').App
|
|
|
1007
1007
|
const input = record(value);
|
|
1008
1008
|
exactKeys(
|
|
1009
1009
|
input,
|
|
1010
|
-
['post_id', 'message', '
|
|
1011
|
-
['post_id', 'message'],
|
|
1010
|
+
['post_id', 'message', 'to', 'class', 'documents'],
|
|
1011
|
+
['post_id', 'message', 'to'],
|
|
1012
1012
|
);
|
|
1013
1013
|
const output: import('./types.js').AppendLogRequest = {
|
|
1014
1014
|
post_id: decodeUuid(input.post_id, ['post_id']),
|
|
1015
1015
|
message: boundedString(input.message, 1, PROTOCOL_LIMIT_CEILINGS.max_log_message_bytes, ['message']),
|
|
1016
|
+
to: input.to === 'broadcast' ? 'broadcast' : decodeRecipientSelectors(input.to),
|
|
1016
1017
|
};
|
|
1017
|
-
if (input.visibility !== undefined) {
|
|
1018
|
-
if (input.visibility !== 'broadcast' && input.visibility !== 'direct') {
|
|
1019
|
-
fail('Invalid log visibility.', ['visibility']);
|
|
1020
|
-
}
|
|
1021
|
-
output.visibility = input.visibility;
|
|
1022
|
-
}
|
|
1023
|
-
if (input.recipientDroneIds !== undefined) {
|
|
1024
|
-
if (!Array.isArray(input.recipientDroneIds) || input.recipientDroneIds.length === 0 || input.recipientDroneIds.length > 100) {
|
|
1025
|
-
fail('Expected recipientDroneIds to contain 1-100 UUIDs.', ['recipientDroneIds']);
|
|
1026
|
-
}
|
|
1027
|
-
output.recipientDroneIds = input.recipientDroneIds.map((id, index) =>
|
|
1028
|
-
decodeUuid(id, ['recipientDroneIds', index])
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1031
1018
|
if (input.class !== undefined) {
|
|
1032
1019
|
output.class = boundedString(input.class, 1, 64, ['class']);
|
|
1033
1020
|
}
|
|
1034
|
-
if (input.to !== undefined) {
|
|
1035
|
-
output.to = decodeStringArray(input.to, 'to', 100, 120);
|
|
1036
|
-
}
|
|
1037
1021
|
if (input.documents !== undefined) {
|
|
1038
1022
|
output.documents = decodeStringArray(input.documents, 'documents', 100, 128)
|
|
1039
1023
|
.map((id, index) => decodeOpaqueIdentifier(id, ['documents', index]));
|
|
@@ -1041,6 +1025,16 @@ export function decodeAppendLogRequest(value: unknown): import('./types.js').App
|
|
|
1041
1025
|
return output;
|
|
1042
1026
|
}
|
|
1043
1027
|
|
|
1028
|
+
function decodeRecipientSelectors(value: unknown): string[] {
|
|
1029
|
+
const selectors = decodeStringArray(value, 'to', 100, 120);
|
|
1030
|
+
for (const [index, selector] of selectors.entries()) {
|
|
1031
|
+
if (selector !== selector.trim() || /[\u0000-\u001f\u007f-\u009f]/.test(selector)) {
|
|
1032
|
+
fail('Recipient selectors must be trimmed and control-free.', ['to', index]);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
return selectors;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1044
1038
|
function decodeStringArray(value: unknown, field: string, maxItems: number, maxLength: number): string[] {
|
|
1045
1039
|
if (!Array.isArray(value) || value.length === 0 || value.length > maxItems) {
|
|
1046
1040
|
fail(`Expected ${field} to contain 1-${maxItems} values.`, [field]);
|
|
@@ -47,6 +47,14 @@ export interface ReadLogResult {
|
|
|
47
47
|
claims: ClaimRecord[];
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export interface EntryQueryRequest {
|
|
51
|
+
entry_id: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface EntryQueryResult {
|
|
55
|
+
entry: EnrichedStreamEntry;
|
|
56
|
+
}
|
|
57
|
+
|
|
50
58
|
export interface AckStatusRequest {
|
|
51
59
|
entry_id: string;
|
|
52
60
|
}
|
|
@@ -335,6 +343,32 @@ export function decodeReadLogRequestEnvelope(
|
|
|
335
343
|
return decodeProtocolEnvelope(value, decodeReadLogRequest);
|
|
336
344
|
}
|
|
337
345
|
|
|
346
|
+
export function decodeEntryQueryRequest(value: unknown): EntryQueryRequest {
|
|
347
|
+
const input = object(value);
|
|
348
|
+
exact(input, ['entry_id'], ['entry_id']);
|
|
349
|
+
const entryId = boundedString(input.entry_id, 'entry_id', 36);
|
|
350
|
+
if (/^[0-9a-f]{8}$/i.test(entryId)) return { entry_id: entryId.toLowerCase() };
|
|
351
|
+
return { entry_id: decodeUuid(entryId, ['entry_id']) };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function decodeEntryQueryRequestEnvelope(
|
|
355
|
+
value: unknown,
|
|
356
|
+
): ProtocolEnvelope<EntryQueryRequest> {
|
|
357
|
+
return decodeProtocolEnvelope(value, decodeEntryQueryRequest);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function decodeEntryQueryResult(value: unknown): EntryQueryResult {
|
|
361
|
+
const input = object(value);
|
|
362
|
+
exact(input, ['entry'], ['entry']);
|
|
363
|
+
return { entry: decodeEnrichedStreamEntry(input.entry) };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function decodeEntryQueryResultEnvelope(
|
|
367
|
+
value: unknown,
|
|
368
|
+
): ProtocolEnvelope<EntryQueryResult> {
|
|
369
|
+
return decodeProtocolEnvelope(value, decodeEntryQueryResult);
|
|
370
|
+
}
|
|
371
|
+
|
|
338
372
|
export function decodeAckStatusRequest(value: unknown): AckStatusRequest {
|
|
339
373
|
const input = object(value);
|
|
340
374
|
exact(input, ['entry_id'], ['entry_id']);
|
|
@@ -445,22 +479,17 @@ function decodeRoutingEcho(value: unknown): RoutingEcho {
|
|
|
445
479
|
const input = object(value);
|
|
446
480
|
exact(
|
|
447
481
|
input,
|
|
448
|
-
['class', 'recipients'
|
|
449
|
-
['class', 'recipients'
|
|
482
|
+
['class', 'recipients'],
|
|
483
|
+
['class', 'recipients'],
|
|
450
484
|
);
|
|
451
485
|
if (!Array.isArray(input.recipients) || input.recipients.length > 100) {
|
|
452
486
|
throw new ProtocolContractError('Invalid routing recipient list.');
|
|
453
487
|
}
|
|
454
|
-
if (typeof input.fellOpen !== 'boolean') {
|
|
455
|
-
throw new ProtocolContractError('Invalid routing fell-open flag.');
|
|
456
|
-
}
|
|
457
488
|
return {
|
|
458
489
|
class: nullableString(input.class, 'routing.class', 64),
|
|
459
490
|
recipients: input.recipients.map((recipient) =>
|
|
460
491
|
boundedString(recipient, 'routing.recipients', 120)
|
|
461
492
|
),
|
|
462
|
-
fellOpen: input.fellOpen,
|
|
463
|
-
message: nullableString(input.message, 'routing.message', 512),
|
|
464
493
|
};
|
|
465
494
|
}
|
|
466
495
|
|
package/src/protocol/errors.ts
CHANGED
|
@@ -15,10 +15,10 @@ export enum ErrorCode {
|
|
|
15
15
|
REPOSITORY_ALREADY_ASSOCIATED = 'REPOSITORY_ALREADY_ASSOCIATED',
|
|
16
16
|
CUBE_ALREADY_ASSOCIATED = 'CUBE_ALREADY_ASSOCIATED',
|
|
17
17
|
POST_ID_CONFLICT = 'POST_ID_CONFLICT',
|
|
18
|
+
LOG_ENTRY_PREFIX_AMBIGUOUS = 'LOG_ENTRY_PREFIX_AMBIGUOUS',
|
|
18
19
|
ROLE_IN_USE = 'ROLE_IN_USE',
|
|
19
20
|
DEFAULT_ROLE_REQUIRED = 'DEFAULT_ROLE_REQUIRED',
|
|
20
21
|
ROLE_REQUIRED = 'ROLE_REQUIRED',
|
|
21
|
-
ROLE_REFERENCED = 'ROLE_REFERENCED',
|
|
22
22
|
ROLE_NOT_FOUND = 'ROLE_NOT_FOUND',
|
|
23
23
|
ROLE_SECTION_NOT_FOUND = 'ROLE_SECTION_NOT_FOUND',
|
|
24
24
|
ROLE_HAS_FROZEN_DRONES = 'ROLE_HAS_FROZEN_DRONES',
|
package/src/protocol/types.ts
CHANGED
|
@@ -168,17 +168,13 @@ export interface ReadLogResponse {
|
|
|
168
168
|
export interface RoutingEcho {
|
|
169
169
|
class: string | null;
|
|
170
170
|
recipients: string[];
|
|
171
|
-
fellOpen: boolean;
|
|
172
|
-
message: string | null;
|
|
173
171
|
}
|
|
174
172
|
|
|
175
173
|
export interface AppendLogRequest {
|
|
176
174
|
post_id: string;
|
|
177
175
|
message: string;
|
|
178
|
-
|
|
179
|
-
recipientDroneIds?: string[];
|
|
176
|
+
to: 'broadcast' | string[];
|
|
180
177
|
class?: string;
|
|
181
|
-
to?: string[];
|
|
182
178
|
documents?: string[];
|
|
183
179
|
}
|
|
184
180
|
|
package/src/protocol/version.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Current Borg coordination protocol generation. Clean-slate
|
|
2
|
-
export const PROTOCOL_VERSION = '
|
|
1
|
+
/** Current Borg coordination protocol generation. Clean-slate v12. */
|
|
2
|
+
export const PROTOCOL_VERSION = '12' as const;
|
|
3
3
|
|
|
4
4
|
export type ProtocolVersion = typeof PROTOCOL_VERSION;
|
package/src/templates.ts
CHANGED
|
@@ -21,8 +21,6 @@ export interface TemplateRole {
|
|
|
21
21
|
export interface MessageTaxonomyClass {
|
|
22
22
|
class: string;
|
|
23
23
|
prefixes?: string[];
|
|
24
|
-
routing: 'broadcast' | 'directed';
|
|
25
|
-
default_to?: string[];
|
|
26
24
|
lifecycle?: 'dispatch' | 'completion';
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -156,7 +154,7 @@ Same-repository workflow policy:
|
|
|
156
154
|
- Run \`git remote get-url origin\` to determine whether a hosted origin exists.
|
|
157
155
|
- When that command succeeds, publish the branch with \`git push -u origin <branch>\`; the branch is REVIEW-READY only after that push and exact remote-head verification.
|
|
158
156
|
- When that command fails because no origin exists, omit the push; the work is REVIEW-READY when its exact commit SHA is available through the project review mechanism.
|
|
159
|
-
- After every merge to the protected or main branch,
|
|
157
|
+
- After every merge to the protected or main branch, post the merge SHA with \`to: "broadcast"\`.`;
|
|
160
158
|
|
|
161
159
|
export const UNIVERSAL_SAFETY_DISCIPLINES = [WAKE_PATH_MONITOR_DISCIPLINE];
|
|
162
160
|
|
|
@@ -179,9 +177,10 @@ Drone addressing:
|
|
|
179
177
|
const STRUCTURED_MESSAGE_ROUTING_DISCIPLINE = `
|
|
180
178
|
|
|
181
179
|
Structured message routing:
|
|
182
|
-
-
|
|
180
|
+
- Every borg_log call must set structured \`to:\` to either \`"broadcast"\` or a non-empty recipient selector array.
|
|
181
|
+
- Use \`to: "broadcast"\` only when every cube member is the intended audience; otherwise name every intended recipient explicitly.
|
|
183
182
|
- Naming a recipient inside the message text does not route it.
|
|
184
|
-
-
|
|
183
|
+
- Message classes and prefixes classify lifecycle signals only; they never choose recipients or provide a default audience.`;
|
|
185
184
|
|
|
186
185
|
const DIRECTED_DISCUSSION_DISCIPLINE = `
|
|
187
186
|
- Use QUESTION, ANSWER, or HEADS-UP with \`to:\` for directed discussion outside the role's terminal workflow signals.`;
|
|
@@ -215,82 +214,57 @@ const SOFTWARE_DEV_TAXONOMY: MessageTaxonomy = [
|
|
|
215
214
|
{
|
|
216
215
|
class: 'status-claim',
|
|
217
216
|
prefixes: ['STARTING', 'PROGRESS', 'ACK', 'PONG', 'PUSHING'],
|
|
218
|
-
routing: 'directed',
|
|
219
|
-
default_to: ['coordinator', 'queen'],
|
|
220
217
|
},
|
|
221
218
|
{
|
|
222
219
|
class: 'completion-status',
|
|
223
220
|
prefixes: ['DONE'],
|
|
224
|
-
routing: 'directed',
|
|
225
|
-
default_to: ['coordinator', 'queen'],
|
|
226
221
|
lifecycle: 'completion',
|
|
227
222
|
},
|
|
228
223
|
{
|
|
229
224
|
class: 'review-request',
|
|
230
225
|
prefixes: ['REVIEW-READY'],
|
|
231
|
-
routing: 'directed',
|
|
232
|
-
default_to: ['coordinator', 'queen'],
|
|
233
226
|
},
|
|
234
227
|
{
|
|
235
228
|
class: 'review-feedback',
|
|
236
229
|
prefixes: ['REVIEW-FEEDBACK', 'RQ-FEEDBACK', 'SECURITY-FEEDBACK', 'PD-FEEDBACK', 'PS-FEEDBACK'],
|
|
237
|
-
routing: 'directed',
|
|
238
|
-
default_to: ['coordinator', 'queen'],
|
|
239
230
|
},
|
|
240
231
|
{
|
|
241
232
|
class: 'completion-gate',
|
|
242
233
|
prefixes: ['REVIEW-APPROVED', 'RQ-APPROVED', 'SECURITY-APPROVED', 'PD-APPROVED', 'PS-APPROVED'],
|
|
243
|
-
routing: 'directed',
|
|
244
|
-
default_to: ['coordinator', 'queen'],
|
|
245
234
|
lifecycle: 'completion',
|
|
246
235
|
},
|
|
247
236
|
{
|
|
248
237
|
class: 'blocked-signal',
|
|
249
238
|
prefixes: ['BLOCKED'],
|
|
250
|
-
routing: 'directed',
|
|
251
|
-
default_to: ['coordinator', 'queen'],
|
|
252
239
|
},
|
|
253
240
|
{
|
|
254
241
|
class: 'dispatch-routing',
|
|
255
242
|
prefixes: ['START NOW', 'RESUME NOW', 'REVIEW NOW', 'HOLD'],
|
|
256
|
-
routing: 'directed',
|
|
257
|
-
default_to: ['coordinator', 'queen'],
|
|
258
243
|
lifecycle: 'dispatch',
|
|
259
244
|
},
|
|
260
245
|
{
|
|
261
246
|
class: 'ping',
|
|
262
247
|
prefixes: ['PING'],
|
|
263
|
-
routing: 'directed',
|
|
264
|
-
default_to: ['coordinator', 'queen'],
|
|
265
248
|
},
|
|
266
249
|
{
|
|
267
250
|
class: 'peer-question',
|
|
268
251
|
prefixes: ['QUESTION', 'ASK'],
|
|
269
|
-
routing: 'directed',
|
|
270
|
-
default_to: ['coordinator', 'queen'],
|
|
271
252
|
},
|
|
272
253
|
{
|
|
273
254
|
class: 'peer-answer',
|
|
274
255
|
prefixes: ['ANSWER'],
|
|
275
|
-
routing: 'directed',
|
|
276
|
-
default_to: ['coordinator', 'queen'],
|
|
277
256
|
},
|
|
278
257
|
{
|
|
279
258
|
class: 'peer-heads-up',
|
|
280
259
|
prefixes: ['HEADS-UP'],
|
|
281
|
-
routing: 'directed',
|
|
282
|
-
default_to: ['coordinator', 'queen'],
|
|
283
260
|
},
|
|
284
261
|
{
|
|
285
262
|
class: 'finding',
|
|
286
263
|
prefixes: ['PROPOSAL'],
|
|
287
|
-
routing: 'directed',
|
|
288
|
-
default_to: ['coordinator', 'queen'],
|
|
289
264
|
},
|
|
290
265
|
{
|
|
291
266
|
class: 'cube-wide',
|
|
292
267
|
prefixes: ['DECISION', 'HALT', 'MERGED'],
|
|
293
|
-
routing: 'broadcast',
|
|
294
268
|
},
|
|
295
269
|
];
|
|
296
270
|
|
|
@@ -327,7 +301,7 @@ Communication:
|
|
|
327
301
|
- Surface decisions, blockers, asks, and material evidence in the human conversation, not only the cube log.
|
|
328
302
|
- Distinguish read-only findings, proposals, completed actions, and actions awaiting authority.
|
|
329
303
|
- Send START NOW, RESUME NOW, REVIEW NOW, and HOLD with \`to:\` to the named implementer or reviewer. Use \`to:\` for every later directed transition.
|
|
330
|
-
- Send PING with \`to:\` only for a directed liveness check. Use DECISION or HALT only for an intentional cube-wide human-seat message. After an authorized merge,
|
|
304
|
+
- Send PING with \`to:\` only for a directed liveness check. Use DECISION or HALT with \`to: "broadcast"\` only for an intentional cube-wide human-seat message. After an authorized merge, post MERGED with the exact merge SHA and \`to: "broadcast"\`.
|
|
331
305
|
- Keep the primary playbook operational and concise. Delete obsolete, redundant, historical, cautionary, and example-heavy prose; do not relocate it into new runbooks, decisions, contracts, rationale, or case-study archives unless it has a current operational consumer.
|
|
332
306
|
|
|
333
307
|
Builders implement; reviewers review; you coordinate. Integrate only when authorized.${COORDINATOR_FINDING_DISPATCH_DISCIPLINE}${SERIALIZED_REVIEW_ROUNDS_DISCIPLINE}${GIT_OPERATIONAL_DISCIPLINE_COORDINATOR}${PUSH_DISCIPLINE_COORDINATOR}${DRONE_ADDRESSING_CONVENTION}${STRUCTURED_MESSAGE_ROUTING_DISCIPLINE}${DIRECTED_DISCUSSION_DISCIPLINE}${RECEIPT_AND_LIVENESS_DISCIPLINE}${OPERATOR_CONTROLLED_OWNERSHIP_DISCIPLINE}`;
|
|
@@ -508,76 +482,53 @@ const STARTER_TAXONOMY: MessageTaxonomy = [
|
|
|
508
482
|
{
|
|
509
483
|
class: 'status-claim',
|
|
510
484
|
prefixes: ['STARTING', 'PROGRESS', 'ACK', 'PONG'],
|
|
511
|
-
routing: 'directed',
|
|
512
|
-
default_to: ['coordinator', 'queen'],
|
|
513
485
|
},
|
|
514
486
|
{
|
|
515
487
|
class: 'completion-status',
|
|
516
488
|
prefixes: ['DONE'],
|
|
517
|
-
routing: 'directed',
|
|
518
|
-
default_to: ['coordinator', 'queen'],
|
|
519
489
|
lifecycle: 'completion',
|
|
520
490
|
},
|
|
521
491
|
{
|
|
522
492
|
class: 'review-request',
|
|
523
493
|
prefixes: ['REVIEW-READY'],
|
|
524
|
-
routing: 'directed',
|
|
525
|
-
default_to: ['coordinator', 'queen'],
|
|
526
494
|
},
|
|
527
495
|
{
|
|
528
496
|
class: 'review-feedback',
|
|
529
497
|
prefixes: ['FEEDBACK'],
|
|
530
|
-
routing: 'directed',
|
|
531
|
-
default_to: ['coordinator', 'queen'],
|
|
532
498
|
},
|
|
533
499
|
{
|
|
534
500
|
class: 'completion-gate',
|
|
535
501
|
prefixes: ['APPROVED'],
|
|
536
|
-
routing: 'directed',
|
|
537
|
-
default_to: ['coordinator', 'queen'],
|
|
538
502
|
lifecycle: 'completion',
|
|
539
503
|
},
|
|
540
504
|
{
|
|
541
505
|
class: 'blocked-signal',
|
|
542
506
|
prefixes: ['BLOCKED'],
|
|
543
|
-
routing: 'directed',
|
|
544
|
-
default_to: ['coordinator', 'queen'],
|
|
545
507
|
},
|
|
546
508
|
{
|
|
547
509
|
class: 'dispatch-routing',
|
|
548
510
|
prefixes: ['START NOW', 'RESUME NOW', 'REVIEW NOW', 'HOLD'],
|
|
549
|
-
routing: 'directed',
|
|
550
|
-
default_to: ['coordinator', 'queen'],
|
|
551
511
|
lifecycle: 'dispatch',
|
|
552
512
|
},
|
|
553
513
|
{
|
|
554
514
|
class: 'ping',
|
|
555
515
|
prefixes: ['PING'],
|
|
556
|
-
routing: 'directed',
|
|
557
|
-
default_to: ['coordinator', 'queen'],
|
|
558
516
|
},
|
|
559
517
|
{
|
|
560
518
|
class: 'peer-question',
|
|
561
519
|
prefixes: ['QUESTION', 'ASK'],
|
|
562
|
-
routing: 'directed',
|
|
563
|
-
default_to: ['coordinator', 'queen'],
|
|
564
520
|
},
|
|
565
521
|
{
|
|
566
522
|
class: 'peer-answer',
|
|
567
523
|
prefixes: ['ANSWER'],
|
|
568
|
-
routing: 'directed',
|
|
569
|
-
default_to: ['coordinator', 'queen'],
|
|
570
524
|
},
|
|
571
525
|
{
|
|
572
526
|
class: 'peer-heads-up',
|
|
573
527
|
prefixes: ['HEADS-UP'],
|
|
574
|
-
routing: 'directed',
|
|
575
|
-
default_to: ['coordinator', 'queen'],
|
|
576
528
|
},
|
|
577
529
|
{
|
|
578
530
|
class: 'cube-wide',
|
|
579
531
|
prefixes: ['DECISION', 'HALT'],
|
|
580
|
-
routing: 'broadcast',
|
|
581
532
|
},
|
|
582
533
|
];
|
|
583
534
|
|
|
@@ -606,7 +557,7 @@ const STARTER: Template = {
|
|
|
606
557
|
- Questions, findings, proposals, open queues, and spare capacity do not authorize new work.
|
|
607
558
|
- Route completed work to the Reviewer only when review is required.
|
|
608
559
|
- Send START NOW, RESUME NOW, REVIEW NOW, and HOLD with \`to:\` to the named Worker or Reviewer.
|
|
609
|
-
- Send PING with \`to:\` only for a directed liveness check. Use DECISION or HALT only for an intentional cube-wide human-seat message.
|
|
560
|
+
- Send PING with \`to:\` only for a directed liveness check. Use DECISION or HALT with \`to: "broadcast"\` only for an intentional cube-wide human-seat message.
|
|
610
561
|
- Ask the human before rescoping, abandoning, waiving, merging, shipping, publishing, or taking an irreversible action unless already delegated.
|
|
611
562
|
- Waiting is valid when work is complete, blocked, under review, or awaiting authority.${COORDINATOR_FINDING_DISPATCH_DISCIPLINE}${ANTI_PASSIVE_STANDING_DISCIPLINE}${DRONE_ADDRESSING_CONVENTION}${STRUCTURED_MESSAGE_ROUTING_DISCIPLINE}${DIRECTED_DISCUSSION_DISCIPLINE}${RECEIPT_AND_LIVENESS_DISCIPLINE}${OPERATOR_CONTROLLED_OWNERSHIP_DISCIPLINE}`,
|
|
612
563
|
},
|
|
@@ -642,83 +593,58 @@ const LOCAL_MODEL_TAXONOMY: MessageTaxonomy = [
|
|
|
642
593
|
{
|
|
643
594
|
class: 'executor-echo',
|
|
644
595
|
prefixes: ['PACKET-ECHO'],
|
|
645
|
-
routing: 'directed',
|
|
646
|
-
default_to: ['shaper'],
|
|
647
596
|
},
|
|
648
597
|
{
|
|
649
598
|
class: 'executor-refusal',
|
|
650
599
|
prefixes: ['SPEC-GAP'],
|
|
651
|
-
routing: 'directed',
|
|
652
|
-
default_to: ['shaper'],
|
|
653
600
|
},
|
|
654
601
|
{
|
|
655
602
|
class: 'executor-completion',
|
|
656
603
|
prefixes: ['PACKET-DONE'],
|
|
657
|
-
routing: 'directed',
|
|
658
|
-
default_to: ['shaper'],
|
|
659
604
|
lifecycle: 'completion',
|
|
660
605
|
},
|
|
661
606
|
{
|
|
662
607
|
class: 'packet-dispatch',
|
|
663
608
|
prefixes: ['EXECUTE PACKET'],
|
|
664
|
-
routing: 'directed',
|
|
665
|
-
default_to: ['executor'],
|
|
666
609
|
lifecycle: 'dispatch',
|
|
667
610
|
},
|
|
668
611
|
{
|
|
669
612
|
class: 'packet-verdict',
|
|
670
613
|
prefixes: ['ACCEPT', 'REJECT'],
|
|
671
|
-
routing: 'directed',
|
|
672
|
-
default_to: ['executor'],
|
|
673
614
|
},
|
|
674
615
|
{
|
|
675
616
|
class: 'blocked-signal',
|
|
676
617
|
prefixes: ['BLOCKED'],
|
|
677
|
-
routing: 'directed',
|
|
678
|
-
default_to: ['director', 'queen'],
|
|
679
618
|
},
|
|
680
619
|
{
|
|
681
620
|
class: 'review-request',
|
|
682
621
|
prefixes: ['REVIEW-READY'],
|
|
683
|
-
routing: 'directed',
|
|
684
|
-
default_to: ['director', 'queen'],
|
|
685
622
|
},
|
|
686
623
|
{
|
|
687
624
|
class: 'director-dispatch',
|
|
688
625
|
prefixes: ['DISPATCH', 'HOLD'],
|
|
689
|
-
routing: 'directed',
|
|
690
|
-
default_to: ['shaper'],
|
|
691
626
|
lifecycle: 'dispatch',
|
|
692
627
|
},
|
|
693
628
|
{
|
|
694
629
|
class: 'director-approval',
|
|
695
630
|
prefixes: ['APPROVED'],
|
|
696
|
-
routing: 'directed',
|
|
697
|
-
default_to: ['shaper'],
|
|
698
631
|
lifecycle: 'completion',
|
|
699
632
|
},
|
|
700
633
|
{
|
|
701
634
|
class: 'peer-question',
|
|
702
635
|
prefixes: ['QUESTION'],
|
|
703
|
-
routing: 'directed',
|
|
704
|
-
default_to: ['director', 'queen'],
|
|
705
636
|
},
|
|
706
637
|
{
|
|
707
638
|
class: 'peer-answer',
|
|
708
639
|
prefixes: ['ANSWER'],
|
|
709
|
-
routing: 'directed',
|
|
710
|
-
default_to: ['director', 'queen'],
|
|
711
640
|
},
|
|
712
641
|
{
|
|
713
642
|
class: 'peer-heads-up',
|
|
714
643
|
prefixes: ['HEADS-UP'],
|
|
715
|
-
routing: 'directed',
|
|
716
|
-
default_to: ['director', 'queen'],
|
|
717
644
|
},
|
|
718
645
|
{
|
|
719
646
|
class: 'cube-wide',
|
|
720
647
|
prefixes: ['DECISION'],
|
|
721
|
-
routing: 'broadcast',
|
|
722
648
|
},
|
|
723
649
|
];
|
|
724
650
|
|
|
@@ -754,7 +680,7 @@ Direction and verification:
|
|
|
754
680
|
- Never approve work you authored. If the Shaper implemented an unconvertible item, you are its independent verifier.
|
|
755
681
|
|
|
756
682
|
Continuity:
|
|
757
|
-
- Send DISPATCH, HOLD, APPROVED, QUESTION, ANSWER, and HEADS-UP with \`to:\` to the Shaper. Use DECISION only when the message is intentionally cube-wide.
|
|
683
|
+
- Send DISPATCH, HOLD, APPROVED, QUESTION, ANSWER, and HEADS-UP with \`to:\` to the Shaper. Use DECISION with \`to: "broadcast"\` only when the message is intentionally cube-wide.
|
|
758
684
|
- DISPATCH, HOLD, and DECISION are not completion when they leave an authorized follow-on action.
|
|
759
685
|
- After answering an interruption, resume any Director action you can advance in the same turn.
|
|
760
686
|
- Waiting is valid only when no routed Director action or active outcome remains, or while a named Shaper/reviewer/human decision is outstanding and you have no independent action.
|