borgmcp-shared 0.13.0 → 0.14.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/RELEASES.md +8 -0
- package/dist/conformance/adapter.d.ts +6 -0
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +62 -1
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/conformance/index.d.ts +24 -0
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +22 -0
- package/dist/conformance/index.js.map +1 -1
- package/dist/protocol/contract.d.ts +1 -1
- package/dist/protocol/contract.js +2 -2
- package/dist/protocol/coordination.d.ts +26 -1
- package/dist/protocol/coordination.d.ts.map +1 -1
- package/dist/protocol/coordination.js +52 -0
- package/dist/protocol/coordination.js.map +1 -1
- package/dist/protocol/version.d.ts +1 -1
- package/dist/protocol/version.js +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +17 -9
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +10 -0
- package/docs/release-records.json +29 -0
- package/docs/releases/0.13.1.md +7 -0
- package/docs/releases/0.14.0.md +8 -0
- package/package.json +1 -1
- package/src/conformance/adapter.ts +139 -0
- package/src/conformance/index.ts +34 -0
- package/src/protocol/contract.ts +2 -2
- package/src/protocol/coordination.ts +100 -0
- package/src/protocol/version.ts +2 -2
- package/src/templates.ts +18 -9
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
decodeCreateCubeResponseEnvelope,
|
|
7
7
|
decodeDeleteCubeResponseEnvelope,
|
|
8
8
|
decodeAppendLogResultEnvelope,
|
|
9
|
+
decodeAckStatusResultEnvelope,
|
|
9
10
|
decodeDecisionResultEnvelope,
|
|
10
11
|
decodeDecisionsResultEnvelope,
|
|
11
12
|
decodeEnrollmentExchangeResponseEnvelope,
|
|
@@ -119,6 +120,8 @@ export interface ConformanceCubeManagementState {
|
|
|
119
120
|
export interface ConformanceAuthorityState {
|
|
120
121
|
enrolled_clients: number;
|
|
121
122
|
enrollment_claims: number;
|
|
123
|
+
activity_acknowledgements: number;
|
|
124
|
+
activity_claims: number;
|
|
122
125
|
cubes: number;
|
|
123
126
|
roles: number;
|
|
124
127
|
grants: number;
|
|
@@ -304,6 +307,11 @@ export interface ConformanceOperations {
|
|
|
304
307
|
cube: ConformanceCube,
|
|
305
308
|
request: unknown,
|
|
306
309
|
): Promise<ConformanceHttpResponse>;
|
|
310
|
+
ackStatus(
|
|
311
|
+
credential: string,
|
|
312
|
+
cube: ConformanceCube,
|
|
313
|
+
request: unknown,
|
|
314
|
+
): Promise<ConformanceHttpResponse>;
|
|
307
315
|
updateCube(
|
|
308
316
|
credential: string,
|
|
309
317
|
cube: ConformanceCube,
|
|
@@ -383,6 +391,7 @@ export const ADAPTER_CONFORMANCE_FIXTURES = [
|
|
|
383
391
|
{ id: 'sse.replay-live-transition', area: 'sse' },
|
|
384
392
|
{ id: 'cursor.explicit-expiry', area: 'cursor' },
|
|
385
393
|
{ id: 'acks.idempotent', area: 'acks' },
|
|
394
|
+
{ id: 'acks.status-query', area: 'acks' },
|
|
386
395
|
{ id: 'claims.durable-noncursor', area: 'claims' },
|
|
387
396
|
{ id: 'decisions.topic-supersession', area: 'decisions' },
|
|
388
397
|
{ id: 'security.drone-management-authorization', area: 'security' },
|
|
@@ -1994,6 +2003,136 @@ export async function runAdapterConformance(
|
|
|
1994
2003
|
return { first_status: 204, repeated_status: 204, bodyless: true };
|
|
1995
2004
|
});
|
|
1996
2005
|
|
|
2006
|
+
await record('acks.status-query', async () => {
|
|
2007
|
+
const cube = await environment.admin.createCube('ack-status-cube');
|
|
2008
|
+
await environment.admin.grantCube(principalA, cube);
|
|
2009
|
+
const role = await environment.admin.createRole(cube, {
|
|
2010
|
+
roleClass: 'worker',
|
|
2011
|
+
isHumanSeat: false,
|
|
2012
|
+
name: 'Ack Status Worker',
|
|
2013
|
+
});
|
|
2014
|
+
const acknowledgedRecipient = await environment.admin.createDrone(principalA, cube, role);
|
|
2015
|
+
const claimingRecipient = await environment.admin.createDrone(principalA, cube, role);
|
|
2016
|
+
const acknowledgedCredential = await environment.admin.issueManagedDroneSession(acknowledgedRecipient);
|
|
2017
|
+
const claimingCredential = await environment.admin.issueManagedDroneSession(claimingRecipient);
|
|
2018
|
+
const append = await environment.operations.append(
|
|
2019
|
+
credentialA,
|
|
2020
|
+
cube,
|
|
2021
|
+
createProtocolEnvelope('ack-status-append', {
|
|
2022
|
+
post_id: '00000000-0000-4000-8000-000000000310',
|
|
2023
|
+
message: 'START NOW acknowledgement status fixture',
|
|
2024
|
+
visibility: 'direct',
|
|
2025
|
+
recipientDroneIds: [acknowledgedRecipient.id, claimingRecipient.id],
|
|
2026
|
+
}),
|
|
2027
|
+
);
|
|
2028
|
+
expectStatus(append, 201, 'Acknowledgement-status fixture append');
|
|
2029
|
+
const entry = decodeAppendLogResultEnvelope(append.body).payload.entry;
|
|
2030
|
+
|
|
2031
|
+
const beforeMissing = await environment.admin.observeAuthorityState();
|
|
2032
|
+
const missingResponse = await environment.operations.ackStatus(
|
|
2033
|
+
credentialA,
|
|
2034
|
+
cube,
|
|
2035
|
+
createProtocolEnvelope('ack-status-missing', { entry_id: entry.id }),
|
|
2036
|
+
);
|
|
2037
|
+
expectStatus(missingResponse, 200, 'Missing acknowledgement status');
|
|
2038
|
+
const missing = decodeAckStatusResultEnvelope(missingResponse.body).payload;
|
|
2039
|
+
invariant(
|
|
2040
|
+
missing.visibility === 'direct' &&
|
|
2041
|
+
missing.recipients.length === 2 &&
|
|
2042
|
+
missing.recipients.every((recipient) => recipient.acknowledged_at === null) &&
|
|
2043
|
+
missing.claims.length === 0,
|
|
2044
|
+
'Missing acknowledgement was not represented as nullable per-recipient state.',
|
|
2045
|
+
);
|
|
2046
|
+
assertStateDelta(
|
|
2047
|
+
beforeMissing,
|
|
2048
|
+
await environment.admin.observeAuthorityState(),
|
|
2049
|
+
{},
|
|
2050
|
+
'Missing acknowledgement status query',
|
|
2051
|
+
);
|
|
2052
|
+
|
|
2053
|
+
expectStatus(
|
|
2054
|
+
await environment.operations.ack(
|
|
2055
|
+
acknowledgedCredential,
|
|
2056
|
+
cube,
|
|
2057
|
+
createProtocolEnvelope('ack-status-ack', { entry_id: entry.id, kind: 'ack' }),
|
|
2058
|
+
),
|
|
2059
|
+
204,
|
|
2060
|
+
'Acknowledgement-status acknowledgement',
|
|
2061
|
+
);
|
|
2062
|
+
expectStatus(
|
|
2063
|
+
await environment.operations.ack(
|
|
2064
|
+
claimingCredential,
|
|
2065
|
+
cube,
|
|
2066
|
+
createProtocolEnvelope('ack-status-claim', { entry_id: entry.id, kind: 'claim' }),
|
|
2067
|
+
),
|
|
2068
|
+
204,
|
|
2069
|
+
'Acknowledgement-status claim',
|
|
2070
|
+
);
|
|
2071
|
+
const beforeDistinct = await environment.admin.observeAuthorityState();
|
|
2072
|
+
const distinctResponse = await environment.operations.ackStatus(
|
|
2073
|
+
credentialA,
|
|
2074
|
+
cube,
|
|
2075
|
+
createProtocolEnvelope('ack-status-distinct', { entry_id: entry.id }),
|
|
2076
|
+
);
|
|
2077
|
+
expectStatus(distinctResponse, 200, 'Distinct acknowledgement and claim status');
|
|
2078
|
+
const distinct = decodeAckStatusResultEnvelope(distinctResponse.body).payload;
|
|
2079
|
+
const acknowledged = distinct.recipients.filter((recipient) => recipient.acknowledged_at !== null);
|
|
2080
|
+
invariant(
|
|
2081
|
+
acknowledged.length === 1 &&
|
|
2082
|
+
acknowledged[0].drone_id === acknowledgedRecipient.id &&
|
|
2083
|
+
distinct.claims.length === 1 &&
|
|
2084
|
+
distinct.claims[0].drone_id === claimingRecipient.id &&
|
|
2085
|
+
distinct.recipients.find((recipient) => recipient.drone_id === claimingRecipient.id)?.acknowledged_at === null,
|
|
2086
|
+
'Acknowledgement status conflated acknowledgement and claim records.',
|
|
2087
|
+
);
|
|
2088
|
+
assertStateDelta(
|
|
2089
|
+
beforeDistinct,
|
|
2090
|
+
await environment.admin.observeAuthorityState(),
|
|
2091
|
+
{},
|
|
2092
|
+
'Distinct acknowledgement and claim status query',
|
|
2093
|
+
);
|
|
2094
|
+
|
|
2095
|
+
const unreadResponse = await environment.operations.read(
|
|
2096
|
+
credentialA,
|
|
2097
|
+
cube,
|
|
2098
|
+
createProtocolEnvelope('ack-status-unread', { cursor: null, limit: 10 }),
|
|
2099
|
+
);
|
|
2100
|
+
expectStatus(unreadResponse, 200, 'Post-query unread read');
|
|
2101
|
+
const unread = decodeReadLogResultEnvelope(unreadResponse.body).payload;
|
|
2102
|
+
invariant(
|
|
2103
|
+
unread.entries.length === 1 && unread.entries[0].id === entry.id,
|
|
2104
|
+
'Acknowledgement-status query advanced unread delivery state.',
|
|
2105
|
+
);
|
|
2106
|
+
|
|
2107
|
+
const beforeUnknown = await environment.admin.observeAuthorityState();
|
|
2108
|
+
expectError(
|
|
2109
|
+
await environment.operations.ackStatus(
|
|
2110
|
+
credentialA,
|
|
2111
|
+
cube,
|
|
2112
|
+
createProtocolEnvelope('ack-status-unknown', {
|
|
2113
|
+
entry_id: '00000000-0000-4000-8000-000000000311',
|
|
2114
|
+
}),
|
|
2115
|
+
),
|
|
2116
|
+
404,
|
|
2117
|
+
ErrorCode.NOT_FOUND,
|
|
2118
|
+
'Unknown acknowledgement-status entry',
|
|
2119
|
+
);
|
|
2120
|
+
assertStateDelta(
|
|
2121
|
+
beforeUnknown,
|
|
2122
|
+
await environment.admin.observeAuthorityState(),
|
|
2123
|
+
{},
|
|
2124
|
+
'Unknown acknowledgement status query',
|
|
2125
|
+
);
|
|
2126
|
+
return {
|
|
2127
|
+
missing_acknowledgement: null,
|
|
2128
|
+
acknowledgements: 1,
|
|
2129
|
+
claims: 1,
|
|
2130
|
+
distinct: true,
|
|
2131
|
+
unread_entry_available: true,
|
|
2132
|
+
unknown_entry_code: ErrorCode.NOT_FOUND,
|
|
2133
|
+
};
|
|
2134
|
+
});
|
|
2135
|
+
|
|
1997
2136
|
await record('claims.durable-noncursor', async () => {
|
|
1998
2137
|
invariant(entries[1] && liveCursor, 'Log fixtures did not produce claim state.');
|
|
1999
2138
|
const claim = await environment.operations.ack(
|
package/src/conformance/index.ts
CHANGED
|
@@ -1012,6 +1012,40 @@ export const ROLE_RATIONALE_CONFORMANCE: readonly RoleRationaleConformanceVector
|
|
|
1012
1012
|
},
|
|
1013
1013
|
];
|
|
1014
1014
|
|
|
1015
|
+
export interface AckStatusConformanceVector {
|
|
1016
|
+
name: string;
|
|
1017
|
+
fixture: 'missing-ack' | 'ack-and-claim' | 'unread-preserved' | 'unknown-entry';
|
|
1018
|
+
expected:
|
|
1019
|
+
| { status: 200; acknowledged_at: null; mutation: 'none' }
|
|
1020
|
+
| { status: 200; acknowledgements: 1; claims: 1; distinct: true; mutation: 'none' }
|
|
1021
|
+
| { status: 200; unread_entry_available: true; mutation: 'none' }
|
|
1022
|
+
| { status: 404; error: 'NOT_FOUND'; mutation: 'none' };
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/** Portable acknowledgement-status outcomes for one exact log entry. */
|
|
1026
|
+
export const ACK_STATUS_CONFORMANCE: readonly AckStatusConformanceVector[] = [
|
|
1027
|
+
{
|
|
1028
|
+
name: 'reports an intended recipient with no acknowledgement',
|
|
1029
|
+
fixture: 'missing-ack',
|
|
1030
|
+
expected: { status: 200, acknowledged_at: null, mutation: 'none' },
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
name: 'keeps acknowledgements and claims in separate result collections',
|
|
1034
|
+
fixture: 'ack-and-claim',
|
|
1035
|
+
expected: { status: 200, acknowledgements: 1, claims: 1, distinct: true, mutation: 'none' },
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
name: 'does not consume the queried entry from unread delivery',
|
|
1039
|
+
fixture: 'unread-preserved',
|
|
1040
|
+
expected: { status: 200, unread_entry_available: true, mutation: 'none' },
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
name: 'returns not found for an unknown entry instead of missing acknowledgement state',
|
|
1044
|
+
fixture: 'unknown-entry',
|
|
1045
|
+
expected: { status: 404, error: 'NOT_FOUND', mutation: 'none' },
|
|
1046
|
+
},
|
|
1047
|
+
];
|
|
1048
|
+
|
|
1015
1049
|
/** One canonical corpus consumed unchanged by shared, server, and client tests. */
|
|
1016
1050
|
export const RUNTIME_METADATA_REPOSITORY_CONFORMANCE:
|
|
1017
1051
|
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 = '0.14.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 v11. 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
|
);
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
AppendLogResponse,
|
|
17
17
|
Decision,
|
|
18
18
|
EnrichedStreamEntry,
|
|
19
|
+
LogVisibility,
|
|
19
20
|
RoutingEcho,
|
|
20
21
|
} from './types.js';
|
|
21
22
|
import { isLabelLine, parseRoleSections } from '../role-section.js';
|
|
@@ -46,6 +47,31 @@ export interface ReadLogResult {
|
|
|
46
47
|
claims: ClaimRecord[];
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
export interface AckStatusRequest {
|
|
51
|
+
entry_id: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface AckStatusRecipient {
|
|
55
|
+
drone_id: string;
|
|
56
|
+
drone_label: string | null;
|
|
57
|
+
drone_role: string | null;
|
|
58
|
+
acknowledged_at: string | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface AckStatusClaim {
|
|
62
|
+
drone_id: string;
|
|
63
|
+
drone_label: string | null;
|
|
64
|
+
drone_role: string | null;
|
|
65
|
+
claimed_at: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AckStatusResult {
|
|
69
|
+
entry_id: string;
|
|
70
|
+
visibility: LogVisibility;
|
|
71
|
+
recipients: AckStatusRecipient[];
|
|
72
|
+
claims: AckStatusClaim[];
|
|
73
|
+
}
|
|
74
|
+
|
|
49
75
|
export interface ReassignDroneRequest {
|
|
50
76
|
role_id: string;
|
|
51
77
|
}
|
|
@@ -309,6 +335,80 @@ export function decodeReadLogRequestEnvelope(
|
|
|
309
335
|
return decodeProtocolEnvelope(value, decodeReadLogRequest);
|
|
310
336
|
}
|
|
311
337
|
|
|
338
|
+
export function decodeAckStatusRequest(value: unknown): AckStatusRequest {
|
|
339
|
+
const input = object(value);
|
|
340
|
+
exact(input, ['entry_id'], ['entry_id']);
|
|
341
|
+
return { entry_id: decodeUuid(input.entry_id, ['entry_id']) };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function decodeAckStatusRequestEnvelope(
|
|
345
|
+
value: unknown,
|
|
346
|
+
): ProtocolEnvelope<AckStatusRequest> {
|
|
347
|
+
return decodeProtocolEnvelope(value, decodeAckStatusRequest);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function decodeAckStatusRecipient(value: unknown): AckStatusRecipient {
|
|
351
|
+
const input = object(value);
|
|
352
|
+
exact(
|
|
353
|
+
input,
|
|
354
|
+
['drone_id', 'drone_label', 'drone_role', 'acknowledged_at'],
|
|
355
|
+
['drone_id', 'drone_label', 'drone_role', 'acknowledged_at'],
|
|
356
|
+
);
|
|
357
|
+
return {
|
|
358
|
+
drone_id: decodeUuid(input.drone_id, ['drone_id']),
|
|
359
|
+
drone_label: nullableString(input.drone_label, 'drone_label', 120),
|
|
360
|
+
drone_role: nullableString(input.drone_role, 'drone_role', 120),
|
|
361
|
+
acknowledged_at: input.acknowledged_at === null
|
|
362
|
+
? null
|
|
363
|
+
: decodeCanonicalTimestamp(input.acknowledged_at, ['acknowledged_at']),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function decodeAckStatusClaim(value: unknown): AckStatusClaim {
|
|
368
|
+
const input = object(value);
|
|
369
|
+
exact(
|
|
370
|
+
input,
|
|
371
|
+
['drone_id', 'drone_label', 'drone_role', 'claimed_at'],
|
|
372
|
+
['drone_id', 'drone_label', 'drone_role', 'claimed_at'],
|
|
373
|
+
);
|
|
374
|
+
return {
|
|
375
|
+
drone_id: decodeUuid(input.drone_id, ['drone_id']),
|
|
376
|
+
drone_label: nullableString(input.drone_label, 'drone_label', 120),
|
|
377
|
+
drone_role: nullableString(input.drone_role, 'drone_role', 120),
|
|
378
|
+
claimed_at: decodeCanonicalTimestamp(input.claimed_at, ['claimed_at']),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function decodeAckStatusResult(value: unknown): AckStatusResult {
|
|
383
|
+
const input = object(value);
|
|
384
|
+
exact(
|
|
385
|
+
input,
|
|
386
|
+
['entry_id', 'visibility', 'recipients', 'claims'],
|
|
387
|
+
['entry_id', 'visibility', 'recipients', 'claims'],
|
|
388
|
+
);
|
|
389
|
+
if (input.visibility !== 'broadcast' && input.visibility !== 'direct') {
|
|
390
|
+
throw new ProtocolContractError('Invalid acknowledgement-status visibility.');
|
|
391
|
+
}
|
|
392
|
+
if (!Array.isArray(input.recipients) || input.recipients.length > 500) {
|
|
393
|
+
throw new ProtocolContractError('Invalid acknowledgement-status recipients.');
|
|
394
|
+
}
|
|
395
|
+
if (!Array.isArray(input.claims) || input.claims.length > 500) {
|
|
396
|
+
throw new ProtocolContractError('Invalid acknowledgement-status claims.');
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
entry_id: decodeUuid(input.entry_id, ['entry_id']),
|
|
400
|
+
visibility: input.visibility,
|
|
401
|
+
recipients: input.recipients.map(decodeAckStatusRecipient),
|
|
402
|
+
claims: input.claims.map(decodeAckStatusClaim),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export function decodeAckStatusResultEnvelope(
|
|
407
|
+
value: unknown,
|
|
408
|
+
): ProtocolEnvelope<AckStatusResult> {
|
|
409
|
+
return decodeProtocolEnvelope(value, decodeAckStatusResult);
|
|
410
|
+
}
|
|
411
|
+
|
|
312
412
|
function decodeClaimRecord(value: unknown): ClaimRecord {
|
|
313
413
|
const input = object(value);
|
|
314
414
|
exact(
|
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 v11. */
|
|
2
|
+
export const PROTOCOL_VERSION = '11' as const;
|
|
3
3
|
|
|
4
4
|
export type ProtocolVersion = typeof PROTOCOL_VERSION;
|
package/src/templates.ts
CHANGED
|
@@ -192,6 +192,14 @@ Receipt and liveness:
|
|
|
192
192
|
- Send ACK with \`to:\` to the dispatcher only to confirm receipt; it does not start or complete work.
|
|
193
193
|
- Reply to a directed PING with PONG and \`to:\` to the sender.`;
|
|
194
194
|
|
|
195
|
+
const OPERATOR_CONTROLLED_OWNERSHIP_DISCIPLINE = `
|
|
196
|
+
|
|
197
|
+
Ownership and liveness:
|
|
198
|
+
- Follow active work through concrete milestones from the dispatch and acceptance evidence, not a fixed elapsed-time cadence.
|
|
199
|
+
- If silence or liveness evidence makes status uncertain, send one direct status request and report the evidence to the human.
|
|
200
|
+
- Silence, delay, stale or disconnected state, and missed milestones never authorize rerouting or reassignment.
|
|
201
|
+
- Coordinator, Queen, or Director rerouting or reassignment requires explicit human operator approval for the exact work item and recipient.`;
|
|
202
|
+
|
|
195
203
|
const SOFTWARE_DEV_DIRECTIVE = `## Scope and coordination
|
|
196
204
|
|
|
197
205
|
- The human-authorized outcome, repositories, acceptance criteria, and permitted mutations are the hard boundary.
|
|
@@ -300,9 +308,10 @@ Scope contract:
|
|
|
300
308
|
Activation:
|
|
301
309
|
- Order named drones to start exact authorized work with START NOW, RESUME NOW, REVIEW NOW, or HOLD; name the exact item and first concrete action.
|
|
302
310
|
- ACK and claim are receipt only; neither means work has started or a review is complete.
|
|
303
|
-
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
311
|
+
- When receipt is uncertain, use \`borg_ack-status\` for the routed entry; it reports acknowledgements and claims without advancing unread cursors.
|
|
312
|
+
- Verify activation and progress against the concrete milestones from the dispatch and acceptance evidence.
|
|
313
|
+
- When a milestone is missing and status is uncertain, follow the ownership and liveness discipline. Do not interrupt slow local work merely to satisfy a reporting cadence.
|
|
314
|
+
- Require BLOCKED when safe work stops, naming the missing input while independent work continues.
|
|
306
315
|
- Waiting is valid when work is complete, blocked, under active review, or awaiting human authority. Never manufacture work to avoid idleness.
|
|
307
316
|
|
|
308
317
|
Review:
|
|
@@ -321,7 +330,7 @@ Communication:
|
|
|
321
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, broadcast MERGED with the exact merge SHA.
|
|
322
331
|
- 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.
|
|
323
332
|
|
|
324
|
-
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}`;
|
|
333
|
+
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}`;
|
|
325
334
|
|
|
326
335
|
// Producer minimalism adapts principles from https://github.com/DietrichGebert/ponytail
|
|
327
336
|
// (MIT); this wording is original to Borg MCP.
|
|
@@ -343,7 +352,7 @@ Implementation discipline:
|
|
|
343
352
|
- Mark a deliberate corner-cut with a comment naming the known ceiling and the upgrade path.
|
|
344
353
|
|
|
345
354
|
While working:
|
|
346
|
-
- Post STARTING with the branch and first concrete action.
|
|
355
|
+
- Post STARTING with the branch and first concrete action. Post PROGRESS only when a substantive milestone changes what the Coordinator needs to know. Do not interrupt slow local work merely to satisfy a reporting cadence.
|
|
347
356
|
- Do not add cleanup, broad refactors, speculative hardening, documentation programs, or follow-up issues unless assigned.
|
|
348
357
|
- A discovered issue outside the slice is a finding, not permission to fix it.
|
|
349
358
|
- Add proportionate tests for behavior you change. Run focused verification required by the touched surface, and do not rerun green CI checks merely to duplicate exact-revision evidence.
|
|
@@ -593,13 +602,13 @@ const STARTER: Template = {
|
|
|
593
602
|
|
|
594
603
|
- State the exact work item, boundaries, first action, and completion evidence.
|
|
595
604
|
- Route START NOW, RESUME NOW, REVIEW NOW, or HOLD to a named drone.
|
|
596
|
-
- ACK is receipt only; verify
|
|
605
|
+
- ACK is receipt only; verify activation and progress against concrete milestones from the dispatch and acceptance evidence.
|
|
597
606
|
- Questions, findings, proposals, open queues, and spare capacity do not authorize new work.
|
|
598
607
|
- Route completed work to the Reviewer only when review is required.
|
|
599
608
|
- Send START NOW, RESUME NOW, REVIEW NOW, and HOLD with \`to:\` to the named Worker or Reviewer.
|
|
600
609
|
- Send PING with \`to:\` only for a directed liveness check. Use DECISION or HALT only for an intentional cube-wide human-seat message.
|
|
601
610
|
- Ask the human before rescoping, abandoning, waiving, merging, shipping, publishing, or taking an irreversible action unless already delegated.
|
|
602
|
-
- 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}`,
|
|
611
|
+
- 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}`,
|
|
603
612
|
},
|
|
604
613
|
{
|
|
605
614
|
name: 'Worker',
|
|
@@ -608,7 +617,7 @@ const STARTER: Template = {
|
|
|
608
617
|
detailed_description: `Execute only work explicitly dispatched to you.
|
|
609
618
|
|
|
610
619
|
- Confirm the exact item, boundaries, and expected evidence before changing anything.
|
|
611
|
-
- Post STARTING, perform the smallest coherent task, and report substantive
|
|
620
|
+
- Post STARTING, perform the smallest coherent task, and report PROGRESS when a substantive milestone changes what the Coordinator needs to know. Do not interrupt slow local work merely to satisfy a reporting cadence.
|
|
612
621
|
- Preserve unrelated state. Do not add cleanup, speculative improvements, or follow-up work.
|
|
613
622
|
- If blocked, state the missing input and stop affected mutation; do not silently change the goal.
|
|
614
623
|
- Send STARTING, PROGRESS, DONE, REVIEW-READY, and BLOCKED with \`to:\` to the Coordinator, with the result and verification evidence.
|
|
@@ -749,7 +758,7 @@ Continuity:
|
|
|
749
758
|
- DISPATCH, HOLD, and DECISION are not completion when they leave an authorized follow-on action.
|
|
750
759
|
- After answering an interruption, resume any Director action you can advance in the same turn.
|
|
751
760
|
- 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.
|
|
752
|
-
- An active Director outcome ends with APPROVED, or with BLOCKED naming the missing decision or the reason it cannot proceed. After DECISION, continue with any dispatch or verification that decision enables.${STRUCTURED_MESSAGE_ROUTING_DISCIPLINE}`;
|
|
761
|
+
- An active Director outcome ends with APPROVED, or with BLOCKED naming the missing decision or the reason it cannot proceed. After DECISION, continue with any dispatch or verification that decision enables.${STRUCTURED_MESSAGE_ROUTING_DISCIPLINE}${OPERATOR_CONTROLLED_OWNERSHIP_DISCIPLINE}`;
|
|
753
762
|
|
|
754
763
|
// Producer minimalism adapts principles from https://github.com/DietrichGebert/ponytail
|
|
755
764
|
// (MIT); this wording is original to Borg MCP.
|