borgmcp-shared 0.7.1 → 0.8.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 +8 -33
- package/RELEASES.md +4 -0
- package/dist/conformance/adapter.d.ts +3 -0
- package/dist/conformance/adapter.d.ts.map +1 -1
- package/dist/conformance/adapter.js +29 -2
- package/dist/conformance/adapter.js.map +1 -1
- package/dist/conformance/index.d.ts +2 -0
- package/dist/conformance/index.d.ts.map +1 -1
- package/dist/conformance/index.js +41 -1
- package/dist/conformance/index.js.map +1 -1
- package/dist/protocol/contract.d.ts +14 -1
- package/dist/protocol/contract.d.ts.map +1 -1
- package/dist/protocol/contract.js +181 -1
- package/dist/protocol/contract.js.map +1 -1
- package/dist/templates.d.ts +1 -0
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +14 -3
- package/dist/templates.js.map +1 -1
- package/docs/compatibility.md +6 -1
- package/docs/enrollment.md +5 -5
- package/docs/releasing.md +3 -3
- package/package.json +1 -1
- package/src/conformance/adapter.ts +27 -0
- package/src/conformance/index.ts +49 -1
- package/src/protocol/contract.ts +219 -1
- package/src/templates.ts +15 -3
package/dist/templates.js
CHANGED
|
@@ -82,6 +82,17 @@ export const PUSH_DISCIPLINE_BUILDER = `
|
|
|
82
82
|
Push discipline:
|
|
83
83
|
- Push only the assigned branch after verifying the staged paths and final diff.
|
|
84
84
|
- Do not force-push, rebase a shared branch, or publish from a local substitute artifact.`;
|
|
85
|
+
export const SAME_REPOSITORY_WORKFLOW_DISCIPLINE = `
|
|
86
|
+
|
|
87
|
+
Same-repository worktrees and handover:
|
|
88
|
+
- One seat uses one stable worktree, created once at assimilation under the standard worktree root and approved once by the operator. All seats for a repository are worktrees of the same clone family, sharing its object database and refs.
|
|
89
|
+
- Start each new work item by switching branches in that seat's worktree with \`git checkout -b <branch>\`; never create a new worktree or folder per work item.
|
|
90
|
+
- Create a branch only for a routed work item and announce its name in STARTING. One branch equals one work item and one owning seat; hand a branch to another seat only through an explicit log event.
|
|
91
|
+
- Use merge-only history: no rebases and no force-pushes, because another seat may have the branch checked out or fetched.
|
|
92
|
+
- Hand over a ref and exact commit SHA, never a filesystem path. Reviewers check out the SHA in their own worktree with \`git checkout --detach <SHA>\`; never read another seat's folder. Each review round binds to one exact SHA, and a new SHA restarts the gate sequence.
|
|
93
|
+
- With a hosted origin, push the branch at creation with \`git push -u origin <branch>\`; a branch is cube-visible and REVIEW-READY only after that push.
|
|
94
|
+
- With no hosted remote, the commit itself is the durable handover artifact because clone-family worktrees share refs; omit the push step. If push/fetch semantics are needed locally, use a local bare repository as the origin path.
|
|
95
|
+
- After every merge to the protected or main branch, broadcast the merge SHA. When an origin exists, include \`git fetch origin && git merge origin/main\` as the merge-only sync instruction.`;
|
|
85
96
|
export const UNIVERSAL_SAFETY_DISCIPLINES = [WAKE_PATH_MONITOR_DISCIPLINE];
|
|
86
97
|
export const ROLE_SCOPED_SAFETY_DISCIPLINES = [
|
|
87
98
|
GIT_OPERATIONAL_DISCIPLINE_BUILDER,
|
|
@@ -105,7 +116,7 @@ const SOFTWARE_DEV_DIRECTIVE = `## Scope and coordination
|
|
|
105
116
|
- Reviewers assess the routed exact revision and do not create or expand work.
|
|
106
117
|
- Waiting is valid when no authorized action is available.
|
|
107
118
|
- Merge, deploy, publish, tag, release, credential, and live-operator actions require explicit authority.
|
|
108
|
-
- Keep cube-log signals concise. Put durable reasoning in the relevant issue, change, or existing maintained documentation only when it has an operational consumer
|
|
119
|
+
- Keep cube-log signals concise. Put durable reasoning in the relevant issue, change, or existing maintained documentation only when it has an operational consumer.${SAME_REPOSITORY_WORKFLOW_DISCIPLINE}`;
|
|
109
120
|
const SOFTWARE_DEV_TAXONOMY = [
|
|
110
121
|
{
|
|
111
122
|
class: 'status-claim',
|
|
@@ -401,7 +412,7 @@ const STARTER = {
|
|
|
401
412
|
- Assignment, review, and completion do not authorize unrelated work or integration.
|
|
402
413
|
- ACK is receipt only; STARTING or substantive PROGRESS confirms activation.
|
|
403
414
|
- Findings outside scope are reported, not automatically fixed.
|
|
404
|
-
- Waiting is valid when no authorized action is available
|
|
415
|
+
- Waiting is valid when no authorized action is available.${SAME_REPOSITORY_WORKFLOW_DISCIPLINE}`,
|
|
405
416
|
message_taxonomy: STARTER_TAXONOMY,
|
|
406
417
|
roles: [
|
|
407
418
|
{
|
|
@@ -521,7 +532,7 @@ const LOCAL_MODEL_DIRECTIVE = `## Verification-cost workflow
|
|
|
521
532
|
- A fourth seat is optional: add a second Executor when throughput-bound, or a second capable Director as an independent review lens when correctness-bound. Never use a cheap model as a review lens.
|
|
522
533
|
- Waiting is valid only when no authorized action or active assigned work remains, or while a role is awaiting a named predecessor and has no independent action it can advance.
|
|
523
534
|
- Dispatch, packet echo, status, and answers are not completion. Each role continues its active item in the same turn until it posts a terminal signal from its own vocabulary.
|
|
524
|
-
- Merge, publish, deploy, tag, release, credential, and irreversible actions require explicit authority
|
|
535
|
+
- Merge, publish, deploy, tag, release, credential, and irreversible actions require explicit authority.${SAME_REPOSITORY_WORKFLOW_DISCIPLINE}`;
|
|
525
536
|
const LOCAL_MODEL_DIRECTOR = `You own authorized intent, priorities, decisions, and verification that requires careful reading. Never implement a change.
|
|
526
537
|
|
|
527
538
|
Scope and authority:
|
package/dist/templates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAwCA,MAAM,CAAC,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAEhE,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C;QACE,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,sBAAsB;QAC7B,iBAAiB,EAAE,oCAAoC;KACxD;IACD;QACE,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,iBAAiB,EAAE,qCAAqC;KACzD;IACD;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,aAAa;QACpB,iBAAiB,EAAE,mFAAmF;KACvG;CACO,CAAC;AAEX,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;yFAOoD,CAAC;AAI1F,MAAM,CAAC,MAAM,gCAAgC,GAAG;;;;;;iFAMiC,CAAC;AAElF,MAAM,CAAC,MAAM,mCAAmC,GAAG;;;;;;;;8GAQ2D,CAAC;AAE/G,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;iHAM6E,CAAC;AAElH,MAAM,CAAC,MAAM,kCAAkC,GAAG;;;;;6HAK2E,CAAC;AAE9H,MAAM,CAAC,MAAM,sCAAsC,GAAG;;;;;sFAKgC,CAAC;AAEvF,MAAM,CAAC,MAAM,4BAA4B,GAAG;;;;;;0GAM8D,CAAC;AAE3G,MAAM,CAAC,MAAM,gCAAgC,GAAG;;;;;4DAKY,CAAC;AAE7D,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;iFAIsC,CAAC;AAElF,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;0FAImD,CAAC;AAE3F,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAE3E,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,kCAAkC;IAClC,sCAAsC;IACtC,uBAAuB;IACvB,2BAA2B;IAC3B,gCAAgC;IAChC,gCAAgC;IAChC,oBAAoB;CACrB,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;mGAIwD,CAAC;AAEpG,MAAM,sBAAsB,GAAG;;;;;;;;
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAwCA,MAAM,CAAC,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAEhE,MAAM,CAAC,MAAM,+BAA+B,GAAG;IAC7C;QACE,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,sBAAsB;QAC7B,iBAAiB,EAAE,oCAAoC;KACxD;IACD;QACE,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,iBAAiB,EAAE,qCAAqC;KACzD;IACD;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,aAAa;QACpB,iBAAiB,EAAE,mFAAmF;KACvG;CACO,CAAC;AAEX,MAAM,CAAC,MAAM,qBAAqB,GAAG;;;;;;;yFAOoD,CAAC;AAI1F,MAAM,CAAC,MAAM,gCAAgC,GAAG;;;;;;iFAMiC,CAAC;AAElF,MAAM,CAAC,MAAM,mCAAmC,GAAG;;;;;;;;8GAQ2D,CAAC;AAE/G,MAAM,CAAC,MAAM,oBAAoB,GAAG;;;;;;iHAM6E,CAAC;AAElH,MAAM,CAAC,MAAM,kCAAkC,GAAG;;;;;6HAK2E,CAAC;AAE9H,MAAM,CAAC,MAAM,sCAAsC,GAAG;;;;;sFAKgC,CAAC;AAEvF,MAAM,CAAC,MAAM,4BAA4B,GAAG;;;;;;0GAM8D,CAAC;AAE3G,MAAM,CAAC,MAAM,gCAAgC,GAAG;;;;;4DAKY,CAAC;AAE7D,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;iFAIsC,CAAC;AAElF,MAAM,CAAC,MAAM,uBAAuB,GAAG;;;;0FAImD,CAAC;AAE3F,MAAM,CAAC,MAAM,mCAAmC,GAAG;;;;;;;;;;+LAU4I,CAAC;AAEhM,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,4BAA4B,CAAC,CAAC;AAE3E,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,kCAAkC;IAClC,sCAAsC;IACtC,uBAAuB;IACvB,2BAA2B;IAC3B,gCAAgC;IAChC,gCAAgC;IAChC,oBAAoB;CACrB,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;mGAIwD,CAAC;AAEpG,MAAM,sBAAsB,GAAG;;;;;;;;sKAQuI,mCAAmC,EAAE,CAAC;AAE5M,MAAM,qBAAqB,GAAoB;IAC7C;QACE,KAAK,EAAE,cAAc;QACrB,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC;QACrE,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,mBAAmB;QAC1B,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,CAAC;QAC3C,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;QACpC,SAAS,EAAE,YAAY;KACxB;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,CAAC,cAAc,CAAC;QAC1B,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,iBAAiB;QACxB,QAAQ,EAAE,CAAC,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,CAAC;QAC/F,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,iBAAiB;QACxB,QAAQ,EAAE,CAAC,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,CAAC;QAC/F,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;QACpC,SAAS,EAAE,YAAY;KACxB;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,CAAC,SAAS,CAAC;QACrB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,kBAAkB;QACzB,QAAQ,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,CAAC;QAC5F,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;QACpC,SAAS,EAAE,UAAU;KACtB;IACD;QACE,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,CAAC,MAAM,CAAC;QAClB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC;QAChF,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,cAAc;QACrB,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC;QAC/B,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,WAAW;QAClB,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;QAC9B,OAAO,EAAE,WAAW;KACrB;CACF,CAAC;AAEF,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;uFA2BmE,mCAAmC,GAAG,sCAAsC,GAAG,2BAA2B,GAAG,2BAA2B,EAAE,CAAC;AAElO,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;gFAwBgE,kCAAkC,GAAG,uBAAuB,GAAG,qBAAqB,EAAE,CAAC;AAEvK,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;mDAgB6B,mCAAmC,GAAG,qBAAqB,EAAE,CAAC;AAEjH,MAAM,eAAe,GAAG;;;;;;;;wFAQgE,mCAAmC,GAAG,qBAAqB,EAAE,CAAC;AAEtJ,MAAM,cAAc,GAAG;;;;;;;;qDAQ8B,mCAAmC,GAAG,qBAAqB,EAAE,CAAC;AAEnH,MAAM,gBAAgB,GAAG;;;;;;;2DAOkC,qBAAqB,EAAE,CAAC;AAEnF,MAAM,gBAAgB,GAAG;;;;;;;oEAO2C,mCAAmC,GAAG,qBAAqB,EAAE,CAAC;AAElI,MAAM,YAAY,GAAa;IAC7B,GAAG,+BAA+B,CAAC,CAAC,CAAC;IACrC,WAAW,EAAE,0HAA0H;IACvI,cAAc,EAAE,sBAAsB;IACtC,gBAAgB,EAAE,qBAAqB;IACvC,KAAK,EAAE;QACL;YACE,IAAI,EAAE,aAAa;YACnB,YAAY,EAAE,IAAI;YAClB,aAAa,EAAE,IAAI;YACnB,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,gHAAgH;YACnI,oBAAoB,EAAE,WAAW;SAClC;QACD;YACE,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,IAAI;YAChB,iBAAiB,EAAE,kHAAkH;YACrI,oBAAoB,EAAE,OAAO;SAC9B;QACD;YACE,IAAI,EAAE,eAAe;YACrB,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,0GAA0G;YAC7H,oBAAoB,EAAE,aAAa;SACpC;QACD;YACE,IAAI,EAAE,iBAAiB;YACvB,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,8GAA8G;YACjI,oBAAoB,EAAE,eAAe;SACtC;QACD;YACE,IAAI,EAAE,gBAAgB;YACtB,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,yGAAyG;YAC5H,oBAAoB,EAAE,cAAc;SACrC;QACD;YACE,IAAI,EAAE,kBAAkB;YACxB,aAAa,EAAE,IAAI;YACnB,mBAAmB,EAAE,IAAI;YACzB,iBAAiB,EAAE,2FAA2F;YAC9G,oBAAoB,EAAE,gBAAgB;SACvC;QACD;YACE,IAAI,EAAE,kBAAkB;YACxB,aAAa,EAAE,IAAI;YACnB,mBAAmB,EAAE,IAAI;YACzB,iBAAiB,EAAE,iGAAiG;YACpH,oBAAoB,EAAE,gBAAgB;SACvC;KACF;CACF,CAAC;AAEF,MAAM,gBAAgB,GAAoB;IACxC;QACE,KAAK,EAAE,cAAc;QACrB,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC;QAC1D,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,mBAAmB;QAC1B,QAAQ,EAAE,CAAC,MAAM,CAAC;QAClB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;QACpC,SAAS,EAAE,YAAY;KACxB;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,CAAC,cAAc,CAAC;QAC1B,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,iBAAiB;QACxB,QAAQ,EAAE,CAAC,UAAU,CAAC;QACtB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,iBAAiB;QACxB,QAAQ,EAAE,CAAC,UAAU,CAAC;QACtB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;QACpC,SAAS,EAAE,YAAY;KACxB;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,CAAC,SAAS,CAAC;QACrB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,kBAAkB;QACzB,QAAQ,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,CAAC;QACjF,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;QACpC,SAAS,EAAE,UAAU;KACtB;IACD;QACE,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,CAAC,MAAM,CAAC;QAClB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC;KACrC;IACD;QACE,KAAK,EAAE,WAAW;QAClB,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;QAC9B,OAAO,EAAE,WAAW;KACrB;CACF,CAAC;AAEF,MAAM,OAAO,GAAa;IACxB,GAAG,+BAA+B,CAAC,CAAC,CAAC;IACrC,WAAW,EAAE,mGAAmG;IAChH,cAAc,EAAE;;;;;;4DAM0C,mCAAmC,EAAE;IAC/F,gBAAgB,EAAE,gBAAgB;IAClC,KAAK,EAAE;QACL;YACE,IAAI,EAAE,aAAa;YACnB,aAAa,EAAE,IAAI;YACnB,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,oGAAoG;YACvH,oBAAoB,EAAE;;;;;;;;yFAQ6D,gCAAgC,GAAG,2BAA2B,EAAE;SACpJ;QACD;YACE,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,IAAI;YAChB,iBAAiB,EAAE,wFAAwF;YAC3G,oBAAoB,EAAE;;;;;;;qEAOyC,qBAAqB,EAAE;SACvF;QACD;YACE,IAAI,EAAE,UAAU;YAChB,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,iGAAiG;YACpH,oBAAoB,EAAE;;;;;;8CAMkB,qBAAqB,EAAE;SAChE;KACF;CACF,CAAC;AAEF,MAAM,oBAAoB,GAAoB;IAC5C;QACE,KAAK,EAAE,eAAe;QACtB,QAAQ,EAAE,CAAC,aAAa,CAAC;QACzB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,QAAQ,CAAC;KACvB;IACD;QACE,KAAK,EAAE,kBAAkB;QACzB,QAAQ,EAAE,CAAC,UAAU,CAAC;QACtB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,QAAQ,CAAC;KACvB;IACD;QACE,KAAK,EAAE,qBAAqB;QAC5B,QAAQ,EAAE,CAAC,aAAa,CAAC;QACzB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,QAAQ,CAAC;QACtB,SAAS,EAAE,YAAY;KACxB;IACD;QACE,KAAK,EAAE,iBAAiB;QACxB,QAAQ,EAAE,CAAC,gBAAgB,CAAC;QAC5B,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,UAAU,CAAC;QACxB,SAAS,EAAE,UAAU;KACtB;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC;QAC9B,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,UAAU,CAAC;KACzB;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,CAAC,SAAS,CAAC;QACrB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC;KAClC;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,QAAQ,EAAE,CAAC,cAAc,CAAC;QAC1B,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,UAAU,EAAE,OAAO,CAAC;KAClC;IACD;QACE,KAAK,EAAE,mBAAmB;QAC1B,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;QAC9B,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,QAAQ,CAAC;QACtB,SAAS,EAAE,UAAU;KACtB;IACD;QACE,KAAK,EAAE,mBAAmB;QAC1B,QAAQ,EAAE,CAAC,UAAU,CAAC;QACtB,OAAO,EAAE,UAAU;QACnB,UAAU,EAAE,CAAC,QAAQ,CAAC;QACtB,SAAS,EAAE,YAAY;KACxB;IACD;QACE,KAAK,EAAE,WAAW;QAClB,QAAQ,EAAE,CAAC,UAAU,CAAC;QACtB,OAAO,EAAE,WAAW;KACrB;CACF,CAAC;AAEF,MAAM,qBAAqB,GAAG;;;;;;;;;;0GAU4E,mCAAmC,EAAE,CAAC;AAEhJ,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;gNAmBmL,CAAC;AAEjN,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;8DA0BmC,CAAC;AAE/D,MAAM,oBAAoB,GAAG;;;;;;;;;;;;;;;;;oIAiBuG,CAAC;AAErI,MAAM,WAAW,GAAa;IAC5B,GAAG,+BAA+B,CAAC,CAAC,CAAC;IACrC,WAAW,EAAE,6GAA6G;IAC1H,cAAc,EAAE,qBAAqB;IACrC,gBAAgB,EAAE,oBAAoB;IACtC,KAAK,EAAE;QACL;YACE,IAAI,EAAE,UAAU;YAChB,YAAY,EAAE,IAAI;YAClB,aAAa,EAAE,IAAI;YACnB,aAAa,EAAE,IAAI;YACnB,iBAAiB,EAAE,yFAAyF;YAC5G,oBAAoB,EAAE,oBAAoB;SAC3C;QACD;YACE,IAAI,EAAE,QAAQ;YACd,iBAAiB,EAAE,0HAA0H;YAC7I,oBAAoB,EAAE,kBAAkB;SACzC;QACD;YACE,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,IAAI;YAChB,iBAAiB,EAAE,qHAAqH;YACxI,oBAAoB,EAAE,oBAAoB;SAC3C;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAA6B;IACjD,cAAc,EAAE,YAAY;IAC5B,OAAO,EAAE,OAAO;IAChB,aAAa,EAAE,WAAW;CAC3B,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,gBAAwB,EACxB,QAAyB;IAEzB,IAAI,gBAAgB,IAAI,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,gBAAgB,CAAC;IAChF,OAAO,QAAQ,EAAE,cAAc,IAAI,gBAAgB,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,oBAA+C,EAC/C,QAAkB;IAElB,IAAI,oBAAoB,IAAI,oBAAoB,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5E,OAAO,QAAQ,CAAC,cAAc,IAAI,IAAI,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,gBAAoD,EACpD,QAAyB;IAEzB,OAAO,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,gBAAgB,IAAI,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC;AAChG,CAAC"}
|
package/docs/compatibility.md
CHANGED
|
@@ -56,7 +56,7 @@ Director/Shaper/Executor template. A v5 peer and a v6 peer reject each other
|
|
|
56
56
|
during credential-free preflight, before an older peer can reject the new
|
|
57
57
|
template later while decoding a cube-creation request.
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Published `borgmcp-shared@0.7.1` carries protocol v7. It adds
|
|
60
60
|
manage-gated `DELETE /api/cubes/:cubeId` with an exact terminal response,
|
|
61
61
|
atomic cascade semantics, and `CUBE_DELETED`. Every connected deleted-cube
|
|
62
62
|
stream receives one strict protocol error envelope as `event: error` before
|
|
@@ -64,6 +64,11 @@ close. Former authorized parent and drone credentials receive
|
|
|
64
64
|
`410 CUBE_DELETED` for that cube, including after authority restart; callers
|
|
65
65
|
that were never authorized still receive non-enumerating `404 NOT_FOUND`.
|
|
66
66
|
|
|
67
|
+
The `borgmcp-shared@0.8.0` release candidate retains protocol v7 and adds the
|
|
68
|
+
v2 invitation artifact: a canonical opaque token carrying the HTTPS endpoint,
|
|
69
|
+
CA SPKI SHA-256 pin, enrollment authority, secret, and integrity binding. The
|
|
70
|
+
strict codec rejects legacy or non-canonical forms before enrollment dispatch.
|
|
71
|
+
|
|
67
72
|
Removing or reinterpreting an existing field is a protocol-breaking change even
|
|
68
73
|
when TypeScript permits it. Implementations must not infer compatibility from a
|
|
69
74
|
successful build alone — they must run the shared conformance suite against the
|
package/docs/enrollment.md
CHANGED
|
@@ -25,11 +25,11 @@ mutation and must never be rerun or moved. `borgmcp-shared@0.6.1`,
|
|
|
25
25
|
`fa8a2dc072d4ffe2a16d5f02576fead822a2f72e`; successful attempt-1 workflow run
|
|
26
26
|
`30169732628` published registry integrity
|
|
27
27
|
`sha512-Wm4b0uoOAw9JCz5OTHD0Q2uXKkeWYdkVksdeZvRG8l62XGMY+G8GkNEsZT9L533LbVbQ29GhgF0htjDenQThDg==`.
|
|
28
|
-
Never rerun or move that tag. `borgmcp-shared@0.7.0`
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
creating `v0.
|
|
32
|
-
reviewed artifact remain separate, independently gated steps.
|
|
28
|
+
Never rerun or move that tag. `borgmcp-shared@0.7.0` and
|
|
29
|
+
`borgmcp-shared@0.7.1` are published and immutable. This source now identifies
|
|
30
|
+
the unpublished `0.8.0` protocol-v7 invitation-artifact release candidate. The
|
|
31
|
+
version bump grants no tag or publication authority: creating `v0.8.0` and
|
|
32
|
+
publishing the reviewed artifact remain separate, independently gated steps.
|
|
33
33
|
There is no compatibility path that returns a bearer from the server.
|
|
34
34
|
|
|
35
35
|
A client verifies the credential-free `GET /api/protocol` tag preflight before
|
package/docs/releasing.md
CHANGED
|
@@ -139,9 +139,9 @@ mutation. Never rerun or move that tag. `borgmcp-shared@0.6.1`,
|
|
|
139
139
|
`fa8a2dc072d4ffe2a16d5f02576fead822a2f72e`; successful attempt-1 workflow run
|
|
140
140
|
`30169732628` published registry integrity
|
|
141
141
|
`sha512-Wm4b0uoOAw9JCz5OTHD0Q2uXKkeWYdkVksdeZvRG8l62XGMY+G8GkNEsZT9L533LbVbQ29GhgF0htjDenQThDg==`.
|
|
142
|
-
Never rerun or move that tag. `borgmcp-shared@0.7.0`
|
|
143
|
-
|
|
144
|
-
release candidate. Its release requires
|
|
142
|
+
Never rerun or move that tag. `borgmcp-shared@0.7.0` and
|
|
143
|
+
`borgmcp-shared@0.7.1` are published and immutable. This source identifies the
|
|
144
|
+
unpublished `0.8.0` protocol-v7 invitation-artifact release candidate. Its release requires
|
|
145
145
|
reviewed source and explicit publication authorization through one protected
|
|
146
146
|
publish run whose terminal boundary is successful `npm publish`. The workflow
|
|
147
147
|
performs no post-publication registry readback. Consumers must update the
|
package/package.json
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
decodeDecisionResultEnvelope,
|
|
10
10
|
decodeDecisionsResultEnvelope,
|
|
11
11
|
decodeEnrollmentExchangeResponseEnvelope,
|
|
12
|
+
decodeInvitationArtifact,
|
|
12
13
|
decodeAttachResponseEnvelope,
|
|
13
14
|
decodeDroneRuntimeMetadataPatch,
|
|
14
15
|
decodeEvictDroneResultEnvelope,
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
import {
|
|
36
37
|
CREATE_CUBE_ASSOCIATION_CONFORMANCE,
|
|
37
38
|
CREATE_CUBE_RETRY_CONFORMANCE,
|
|
39
|
+
INVITATION_ARTIFACT_CONFORMANCE,
|
|
38
40
|
ENROLLMENT_RETRY_CONFORMANCE,
|
|
39
41
|
} from './index.js';
|
|
40
42
|
|
|
@@ -335,6 +337,7 @@ export interface ConformanceEnvironment {
|
|
|
335
337
|
export const ADAPTER_CONFORMANCE_FIXTURES = [
|
|
336
338
|
{ id: 'http.unauthenticated-liveness', area: 'http' },
|
|
337
339
|
{ id: 'protocol.credential-free-preflight', area: 'protocol' },
|
|
340
|
+
{ id: 'enrollment.invitation-artifact', area: 'enrollment' },
|
|
338
341
|
{ id: 'enrollment.retry-authority', area: 'enrollment' },
|
|
339
342
|
{ id: 'repository.explicit-association', area: 'repository' },
|
|
340
343
|
{ id: 'security.adapter-boundary-injection', area: 'security' },
|
|
@@ -642,6 +645,30 @@ export async function runAdapterConformance(
|
|
|
642
645
|
return { authenticated: false, mutation_free: true, protocol_version: preflight.protocol_version };
|
|
643
646
|
});
|
|
644
647
|
|
|
648
|
+
await record('enrollment.invitation-artifact', async () => {
|
|
649
|
+
const errors: string[] = [];
|
|
650
|
+
for (const vector of INVITATION_ARTIFACT_CONFORMANCE) {
|
|
651
|
+
if (vector.expected === null) {
|
|
652
|
+
let rejected = false;
|
|
653
|
+
try {
|
|
654
|
+
decodeInvitationArtifact(vector.input);
|
|
655
|
+
} catch {
|
|
656
|
+
rejected = true;
|
|
657
|
+
}
|
|
658
|
+
if (!rejected) errors.push(`${vector.name}: permissive decoder accepted the invalid artifact.`);
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
try {
|
|
662
|
+
const decoded = decodeInvitationArtifact(vector.input);
|
|
663
|
+
invariant(same(decoded, vector.expected), `${vector.name} decoded a different artifact.`);
|
|
664
|
+
} catch (error) {
|
|
665
|
+
errors.push(`${vector.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
invariant(errors.length === 0, errors.join(' | '));
|
|
669
|
+
return { vectors: INVITATION_ARTIFACT_CONFORMANCE.length };
|
|
670
|
+
});
|
|
671
|
+
|
|
645
672
|
await record('enrollment.retry-authority', async () => {
|
|
646
673
|
const retryVectorErrors: string[] = [];
|
|
647
674
|
for (const [index, vector] of ENROLLMENT_RETRY_CONFORMANCE.entries()) {
|
package/src/conformance/index.ts
CHANGED
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
EnrollmentExchangeRequest,
|
|
8
8
|
ResolveRepositoryCubeRequest,
|
|
9
9
|
} from '../protocol/contract.js';
|
|
10
|
+
import {
|
|
11
|
+
encodeInvitationArtifact,
|
|
12
|
+
type InvitationArtifact,
|
|
13
|
+
} from '../protocol/contract.js';
|
|
10
14
|
import type { EnrichedStreamEntry } from '../protocol/types.js';
|
|
11
15
|
|
|
12
16
|
export * from './adapter.js';
|
|
@@ -230,7 +234,51 @@ export interface EnrollmentRetryConformanceVector {
|
|
|
230
234
|
};
|
|
231
235
|
}
|
|
232
236
|
|
|
233
|
-
const
|
|
237
|
+
const ENROLLMENT_ARTIFACT: InvitationArtifact = {
|
|
238
|
+
version: 2,
|
|
239
|
+
endpoint: 'https://borg.example.test:7091',
|
|
240
|
+
ca_spki_sha256: 'a'.repeat(64),
|
|
241
|
+
authority: 'client',
|
|
242
|
+
secret: 'S'.repeat(43),
|
|
243
|
+
integrity: 'I'.repeat(43),
|
|
244
|
+
};
|
|
245
|
+
const ENROLLMENT_ARTIFACT_TOKEN = encodeInvitationArtifact(ENROLLMENT_ARTIFACT);
|
|
246
|
+
|
|
247
|
+
export const INVITATION_ARTIFACT_CONFORMANCE: readonly ConformanceVector<
|
|
248
|
+
string,
|
|
249
|
+
InvitationArtifact | null
|
|
250
|
+
>[] = [
|
|
251
|
+
{
|
|
252
|
+
name: 'decodes the canonical v2 invitation artifact',
|
|
253
|
+
input: ENROLLMENT_ARTIFACT_TOKEN,
|
|
254
|
+
expected: ENROLLMENT_ARTIFACT,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: 'rejects a legacy opaque invitation before use',
|
|
258
|
+
input: 'I'.repeat(43),
|
|
259
|
+
expected: null,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'rejects a mangled noncanonical invitation token',
|
|
263
|
+
input: `${ENROLLMENT_ARTIFACT_TOKEN}=`,
|
|
264
|
+
expected: null,
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'rejects an invitation token above the outer bound',
|
|
268
|
+
input: 'A'.repeat(1025),
|
|
269
|
+
expected: null,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: 'rejects a token with a duplicate trailing field',
|
|
273
|
+
input: `${ENROLLMENT_ARTIFACT_TOKEN}A`,
|
|
274
|
+
expected: null,
|
|
275
|
+
},
|
|
276
|
+
];
|
|
277
|
+
|
|
278
|
+
const ENROLLMENT_INVITATION = encodeInvitationArtifact({
|
|
279
|
+
...ENROLLMENT_ARTIFACT,
|
|
280
|
+
authority: 'client',
|
|
281
|
+
});
|
|
234
282
|
const ENROLLMENT_CREDENTIAL = 'A'.repeat(43);
|
|
235
283
|
const ENROLLMENT_RETRY_KEY = '00000000-0000-4000-8000-000000000101';
|
|
236
284
|
|
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.8.0' as const;
|
|
17
17
|
|
|
18
18
|
export const HEALTH_PATH = '/healthz' as const;
|
|
19
19
|
export const PROTOCOL_INFO_PATH = '/api/protocol' as const;
|
|
@@ -125,6 +125,23 @@ export interface EnrollmentExchangeRequest {
|
|
|
125
125
|
client_name?: string;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
export const INVITATION_ARTIFACT_VERSION = 2 as const;
|
|
129
|
+
export type InvitationAuthority = 'client' | 'owner';
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The single opaque value transported between machines for enrollment. The
|
|
133
|
+
* integrity field is produced and verified by the issuing implementation; the
|
|
134
|
+
* shared codec preserves it as a bounded canonical field.
|
|
135
|
+
*/
|
|
136
|
+
export interface InvitationArtifact {
|
|
137
|
+
version: typeof INVITATION_ARTIFACT_VERSION;
|
|
138
|
+
endpoint: string;
|
|
139
|
+
ca_spki_sha256: string;
|
|
140
|
+
authority: InvitationAuthority;
|
|
141
|
+
secret: string;
|
|
142
|
+
integrity: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
128
145
|
export const SERVER_CAPABILITIES = ['create_cube'] as const;
|
|
129
146
|
export type ServerCapability = (typeof SERVER_CAPABILITIES)[number];
|
|
130
147
|
|
|
@@ -315,6 +332,207 @@ function opaqueToken(value: unknown, path: readonly (string | number)[]): string
|
|
|
315
332
|
return token;
|
|
316
333
|
}
|
|
317
334
|
|
|
335
|
+
const BASE64URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
336
|
+
const INVITATION_MAGIC = 'B2';
|
|
337
|
+
const INVITATION_LENGTH_HEX_DIGITS = 3;
|
|
338
|
+
|
|
339
|
+
function encodeBase64UrlAscii(value: string): string {
|
|
340
|
+
let output = '';
|
|
341
|
+
for (let index = 0; index < value.length; index += 3) {
|
|
342
|
+
const first = value.charCodeAt(index);
|
|
343
|
+
const hasSecond = index + 1 < value.length;
|
|
344
|
+
const hasThird = index + 2 < value.length;
|
|
345
|
+
const second = hasSecond ? value.charCodeAt(index + 1) : 0;
|
|
346
|
+
const third = hasThird ? value.charCodeAt(index + 2) : 0;
|
|
347
|
+
output += BASE64URL_ALPHABET[first >> 2];
|
|
348
|
+
output += BASE64URL_ALPHABET[((first & 0x03) << 4) | (second >> 4)];
|
|
349
|
+
if (hasSecond) output += BASE64URL_ALPHABET[((second & 0x0f) << 2) | (third >> 6)];
|
|
350
|
+
if (hasThird) output += BASE64URL_ALPHABET[third & 0x3f];
|
|
351
|
+
}
|
|
352
|
+
return output;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function decodeBase64UrlAscii(value: string, path: readonly (string | number)[]): string {
|
|
356
|
+
if (!/^[A-Za-z0-9_-]*$/.test(value) || value.length % 4 === 1) {
|
|
357
|
+
fail('Expected an unpadded base64url value.', path);
|
|
358
|
+
}
|
|
359
|
+
let output = '';
|
|
360
|
+
for (let index = 0; index < value.length; index += 4) {
|
|
361
|
+
const first = BASE64URL_ALPHABET.indexOf(value[index]);
|
|
362
|
+
const second = BASE64URL_ALPHABET.indexOf(value[index + 1]);
|
|
363
|
+
const third = index + 2 < value.length ? BASE64URL_ALPHABET.indexOf(value[index + 2]) : 0;
|
|
364
|
+
const fourth = index + 3 < value.length ? BASE64URL_ALPHABET.indexOf(value[index + 3]) : 0;
|
|
365
|
+
if (first < 0 || second < 0 || (index + 2 < value.length && third < 0) ||
|
|
366
|
+
(index + 3 < value.length && fourth < 0)) {
|
|
367
|
+
fail('Expected an unpadded base64url value.', path);
|
|
368
|
+
}
|
|
369
|
+
const bytes = [
|
|
370
|
+
(first << 2) | (second >> 4),
|
|
371
|
+
((second & 0x0f) << 4) | (third >> 2),
|
|
372
|
+
((third & 0x03) << 6) | fourth,
|
|
373
|
+
];
|
|
374
|
+
const byteCount = Math.min(3, value.length - index - 1);
|
|
375
|
+
for (let byteIndex = 0; byteIndex < byteCount; byteIndex++) {
|
|
376
|
+
if (bytes[byteIndex] > 0x7f) fail('Invitation fields must contain ASCII bytes.', path);
|
|
377
|
+
output += String.fromCharCode(bytes[byteIndex]);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (encodeBase64UrlAscii(output) !== value) {
|
|
381
|
+
fail('Expected canonical unpadded base64url encoding.', path);
|
|
382
|
+
}
|
|
383
|
+
return output;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function encodedBase64UrlLength(byteLength: number): number {
|
|
387
|
+
const remainder = byteLength % 3;
|
|
388
|
+
return Math.floor(byteLength / 3) * 4 + (remainder === 0 ? 0 : remainder + 1);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function invitationFieldLength(value: string, path: readonly (string | number)[]): string {
|
|
392
|
+
const length = value.length;
|
|
393
|
+
if (length > 0xfff) fail('Invitation field is too long.', path);
|
|
394
|
+
return length.toString(16).padStart(INVITATION_LENGTH_HEX_DIGITS, '0');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function decodeInvitationField(
|
|
398
|
+
payload: string,
|
|
399
|
+
cursor: { value: number },
|
|
400
|
+
path: readonly (string | number)[],
|
|
401
|
+
): string {
|
|
402
|
+
const lengthText = payload.slice(cursor.value, cursor.value + INVITATION_LENGTH_HEX_DIGITS);
|
|
403
|
+
if (!/^[0-9a-f]{3}$/.test(lengthText)) fail('Invitation field length is invalid.', path);
|
|
404
|
+
cursor.value += INVITATION_LENGTH_HEX_DIGITS;
|
|
405
|
+
const byteLength = Number.parseInt(lengthText, 16);
|
|
406
|
+
const encodedLength = encodedBase64UrlLength(byteLength);
|
|
407
|
+
const encoded = payload.slice(cursor.value, cursor.value + encodedLength);
|
|
408
|
+
if (encoded.length !== encodedLength) fail('Invitation field is truncated.', path);
|
|
409
|
+
cursor.value += encodedLength;
|
|
410
|
+
const decoded = decodeBase64UrlAscii(encoded, path);
|
|
411
|
+
if (decoded.length !== byteLength) fail('Invitation field length does not match its value.', path);
|
|
412
|
+
return decoded;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function canonicalInvitationEndpoint(value: unknown, path: readonly (string | number)[]): string {
|
|
416
|
+
const endpoint = boundedString(value, 1, 512, path);
|
|
417
|
+
type ParsedUrl = {
|
|
418
|
+
protocol: string;
|
|
419
|
+
origin: string;
|
|
420
|
+
hostname: string;
|
|
421
|
+
port: string;
|
|
422
|
+
pathname: string;
|
|
423
|
+
search: string;
|
|
424
|
+
hash: string;
|
|
425
|
+
username: string;
|
|
426
|
+
password: string;
|
|
427
|
+
};
|
|
428
|
+
const UrlParser = (globalThis as unknown as { URL?: new (value: string) => ParsedUrl }).URL;
|
|
429
|
+
if (UrlParser === undefined) fail('Invitation endpoint URL parsing is unavailable.', path);
|
|
430
|
+
let parsed: ParsedUrl;
|
|
431
|
+
try {
|
|
432
|
+
parsed = new UrlParser(endpoint);
|
|
433
|
+
} catch {
|
|
434
|
+
fail('Invitation endpoint must be a valid URL.', path);
|
|
435
|
+
}
|
|
436
|
+
if (parsed.protocol !== 'https:' || !parsed.hostname || parsed.username || parsed.password ||
|
|
437
|
+
parsed.pathname !== '/' || parsed.search || parsed.hash || endpoint !== parsed.origin ||
|
|
438
|
+
(parsed.port !== '' && (Number.parseInt(parsed.port, 10) < 1 || Number.parseInt(parsed.port, 10) > 65_535))) {
|
|
439
|
+
fail('Invitation endpoint must be a canonical HTTPS origin.', path);
|
|
440
|
+
}
|
|
441
|
+
return endpoint;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function validateInvitationArtifact(value: unknown): InvitationArtifact {
|
|
445
|
+
const input = record(value);
|
|
446
|
+
exactKeys(input, ['version', 'endpoint', 'ca_spki_sha256', 'authority', 'secret', 'integrity'], [
|
|
447
|
+
'version',
|
|
448
|
+
'endpoint',
|
|
449
|
+
'ca_spki_sha256',
|
|
450
|
+
'authority',
|
|
451
|
+
'secret',
|
|
452
|
+
'integrity',
|
|
453
|
+
]);
|
|
454
|
+
if (input.version !== INVITATION_ARTIFACT_VERSION) {
|
|
455
|
+
fail('Unsupported invitation artifact version.', ['version']);
|
|
456
|
+
}
|
|
457
|
+
const endpoint = canonicalInvitationEndpoint(input.endpoint, ['endpoint']);
|
|
458
|
+
const caSpkiSha256 = boundedString(input.ca_spki_sha256, 64, 64, ['ca_spki_sha256']);
|
|
459
|
+
if (!/^[0-9a-f]{64}$/.test(caSpkiSha256)) {
|
|
460
|
+
fail('CA SPKI SHA-256 must be lowercase hexadecimal.', ['ca_spki_sha256']);
|
|
461
|
+
}
|
|
462
|
+
if (input.authority !== 'client' && input.authority !== 'owner') {
|
|
463
|
+
fail('Invitation authority is invalid.', ['authority']);
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
version: INVITATION_ARTIFACT_VERSION,
|
|
467
|
+
endpoint,
|
|
468
|
+
ca_spki_sha256: caSpkiSha256,
|
|
469
|
+
authority: input.authority,
|
|
470
|
+
secret: opaqueToken(input.secret, ['secret']),
|
|
471
|
+
integrity: opaqueToken(input.integrity, ['integrity']),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Return the canonical ASCII preimage for the artifact integrity binding.
|
|
477
|
+
* Implementations hash these exact bytes with their agreed secret algorithm;
|
|
478
|
+
* the shared package intentionally does not own a crypto runtime.
|
|
479
|
+
*/
|
|
480
|
+
export function getInvitationArtifactIntegrityInput(value: InvitationArtifact): string {
|
|
481
|
+
const artifact = validateInvitationArtifact(value);
|
|
482
|
+
const endpoint = encodeBase64UrlAscii(artifact.endpoint);
|
|
483
|
+
const secret = encodeBase64UrlAscii(artifact.secret);
|
|
484
|
+
return [
|
|
485
|
+
INVITATION_MAGIC,
|
|
486
|
+
invitationFieldLength(artifact.endpoint, ['endpoint']),
|
|
487
|
+
endpoint,
|
|
488
|
+
artifact.ca_spki_sha256,
|
|
489
|
+
artifact.authority === 'client' ? 'c' : 'o',
|
|
490
|
+
invitationFieldLength(artifact.secret, ['secret']),
|
|
491
|
+
secret,
|
|
492
|
+
].join('');
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** Encode an invitation artifact as one canonical, unpadded base64url token. */
|
|
496
|
+
export function encodeInvitationArtifact(value: InvitationArtifact): string {
|
|
497
|
+
const artifact = validateInvitationArtifact(value);
|
|
498
|
+
const payload = [
|
|
499
|
+
getInvitationArtifactIntegrityInput(artifact),
|
|
500
|
+
invitationFieldLength(artifact.integrity, ['integrity']),
|
|
501
|
+
encodeBase64UrlAscii(artifact.integrity),
|
|
502
|
+
].join('');
|
|
503
|
+
const token = encodeBase64UrlAscii(payload);
|
|
504
|
+
if (token.length < 43 || token.length > 1024) {
|
|
505
|
+
fail('Encoded invitation artifact exceeds the supported token bound.');
|
|
506
|
+
}
|
|
507
|
+
return token;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/** Decode and strictly validate a canonical invitation artifact token. */
|
|
511
|
+
export function decodeInvitationArtifact(value: unknown): InvitationArtifact {
|
|
512
|
+
const token = opaqueToken(value, ['invitation']);
|
|
513
|
+
const payload = decodeBase64UrlAscii(token, ['invitation']);
|
|
514
|
+
if (!payload.startsWith(INVITATION_MAGIC)) {
|
|
515
|
+
fail('Invitation uses an unsupported or legacy format.', ['invitation']);
|
|
516
|
+
}
|
|
517
|
+
const cursor = { value: INVITATION_MAGIC.length };
|
|
518
|
+
const endpoint = decodeInvitationField(payload, cursor, ['endpoint']);
|
|
519
|
+
const caSpkiSha256 = payload.slice(cursor.value, cursor.value + 64);
|
|
520
|
+
if (caSpkiSha256.length !== 64) fail('Invitation pin is truncated.', ['ca_spki_sha256']);
|
|
521
|
+
cursor.value += 64;
|
|
522
|
+
const authority = payload[cursor.value++];
|
|
523
|
+
const secret = decodeInvitationField(payload, cursor, ['secret']);
|
|
524
|
+
const integrity = decodeInvitationField(payload, cursor, ['integrity']);
|
|
525
|
+
if (cursor.value !== payload.length) fail('Invitation contains trailing fields.', ['invitation']);
|
|
526
|
+
return validateInvitationArtifact({
|
|
527
|
+
version: INVITATION_ARTIFACT_VERSION,
|
|
528
|
+
endpoint,
|
|
529
|
+
ca_spki_sha256: caSpkiSha256,
|
|
530
|
+
authority: authority === 'c' ? 'client' : authority === 'o' ? 'owner' : authority,
|
|
531
|
+
secret,
|
|
532
|
+
integrity,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
318
536
|
function decodeRequestId(value: unknown, path: readonly (string | number)[]): string {
|
|
319
537
|
const decoded = boundedString(value, 8, 128, path);
|
|
320
538
|
if (!/^[A-Za-z0-9._-]+$/.test(decoded)) {
|
package/src/templates.ts
CHANGED
|
@@ -136,6 +136,18 @@ Push discipline:
|
|
|
136
136
|
- Push only the assigned branch after verifying the staged paths and final diff.
|
|
137
137
|
- Do not force-push, rebase a shared branch, or publish from a local substitute artifact.`;
|
|
138
138
|
|
|
139
|
+
export const SAME_REPOSITORY_WORKFLOW_DISCIPLINE = `
|
|
140
|
+
|
|
141
|
+
Same-repository worktrees and handover:
|
|
142
|
+
- One seat uses one stable worktree, created once at assimilation under the standard worktree root and approved once by the operator. All seats for a repository are worktrees of the same clone family, sharing its object database and refs.
|
|
143
|
+
- Start each new work item by switching branches in that seat's worktree with \`git checkout -b <branch>\`; never create a new worktree or folder per work item.
|
|
144
|
+
- Create a branch only for a routed work item and announce its name in STARTING. One branch equals one work item and one owning seat; hand a branch to another seat only through an explicit log event.
|
|
145
|
+
- Use merge-only history: no rebases and no force-pushes, because another seat may have the branch checked out or fetched.
|
|
146
|
+
- Hand over a ref and exact commit SHA, never a filesystem path. Reviewers check out the SHA in their own worktree with \`git checkout --detach <SHA>\`; never read another seat's folder. Each review round binds to one exact SHA, and a new SHA restarts the gate sequence.
|
|
147
|
+
- With a hosted origin, push the branch at creation with \`git push -u origin <branch>\`; a branch is cube-visible and REVIEW-READY only after that push.
|
|
148
|
+
- With no hosted remote, the commit itself is the durable handover artifact because clone-family worktrees share refs; omit the push step. If push/fetch semantics are needed locally, use a local bare repository as the origin path.
|
|
149
|
+
- After every merge to the protected or main branch, broadcast the merge SHA. When an origin exists, include \`git fetch origin && git merge origin/main\` as the merge-only sync instruction.`;
|
|
150
|
+
|
|
139
151
|
export const UNIVERSAL_SAFETY_DISCIPLINES = [WAKE_PATH_MONITOR_DISCIPLINE];
|
|
140
152
|
|
|
141
153
|
export const ROLE_SCOPED_SAFETY_DISCIPLINES = [
|
|
@@ -162,7 +174,7 @@ const SOFTWARE_DEV_DIRECTIVE = `## Scope and coordination
|
|
|
162
174
|
- Reviewers assess the routed exact revision and do not create or expand work.
|
|
163
175
|
- Waiting is valid when no authorized action is available.
|
|
164
176
|
- Merge, deploy, publish, tag, release, credential, and live-operator actions require explicit authority.
|
|
165
|
-
- Keep cube-log signals concise. Put durable reasoning in the relevant issue, change, or existing maintained documentation only when it has an operational consumer
|
|
177
|
+
- Keep cube-log signals concise. Put durable reasoning in the relevant issue, change, or existing maintained documentation only when it has an operational consumer.${SAME_REPOSITORY_WORKFLOW_DISCIPLINE}`;
|
|
166
178
|
|
|
167
179
|
const SOFTWARE_DEV_TAXONOMY: MessageTaxonomy = [
|
|
168
180
|
{
|
|
@@ -469,7 +481,7 @@ const STARTER: Template = {
|
|
|
469
481
|
- Assignment, review, and completion do not authorize unrelated work or integration.
|
|
470
482
|
- ACK is receipt only; STARTING or substantive PROGRESS confirms activation.
|
|
471
483
|
- Findings outside scope are reported, not automatically fixed.
|
|
472
|
-
- Waiting is valid when no authorized action is available
|
|
484
|
+
- Waiting is valid when no authorized action is available.${SAME_REPOSITORY_WORKFLOW_DISCIPLINE}`,
|
|
473
485
|
message_taxonomy: STARTER_TAXONOMY,
|
|
474
486
|
roles: [
|
|
475
487
|
{
|
|
@@ -591,7 +603,7 @@ const LOCAL_MODEL_DIRECTIVE = `## Verification-cost workflow
|
|
|
591
603
|
- A fourth seat is optional: add a second Executor when throughput-bound, or a second capable Director as an independent review lens when correctness-bound. Never use a cheap model as a review lens.
|
|
592
604
|
- Waiting is valid only when no authorized action or active assigned work remains, or while a role is awaiting a named predecessor and has no independent action it can advance.
|
|
593
605
|
- Dispatch, packet echo, status, and answers are not completion. Each role continues its active item in the same turn until it posts a terminal signal from its own vocabulary.
|
|
594
|
-
- Merge, publish, deploy, tag, release, credential, and irreversible actions require explicit authority
|
|
606
|
+
- Merge, publish, deploy, tag, release, credential, and irreversible actions require explicit authority.${SAME_REPOSITORY_WORKFLOW_DISCIPLINE}`;
|
|
595
607
|
|
|
596
608
|
const LOCAL_MODEL_DIRECTOR = `You own authorized intent, priorities, decisions, and verification that requires careful reading. Never implement a change.
|
|
597
609
|
|