superbee 0.4.2-pre.1 → 0.4.2-pre.2
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 +1 -1
- package/dist/superbee.mjs +511 -144
- package/package.json +3 -3
package/dist/superbee.mjs
CHANGED
|
@@ -44,7 +44,7 @@ var __toESM = (mod, isNodeMode, target3) => (target3 = mod != null ? __create(__
|
|
|
44
44
|
var define_SUPERBEE_BUILD_IDENTITY_default;
|
|
45
45
|
var init_define_SUPERBEE_BUILD_IDENTITY = __esm({
|
|
46
46
|
"<define:__SUPERBEE_BUILD_IDENTITY__>"() {
|
|
47
|
-
define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.2-pre.
|
|
47
|
+
define_SUPERBEE_BUILD_IDENTITY_default = { schema: "superbee.build-identity.v1", package: { name: "superbee", version: "0.4.2-pre.2" }, source: { commit: "baa7ac9c51c8c91ae3a17579f19910dddd6a406e", dirty: false }, artifact: { channel: "npm-package" }, compatibility_contracts: { skill: 1, hook: 1, mcp: 1 } };
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -9475,6 +9475,26 @@ var init_mutation = __esm({
|
|
|
9475
9475
|
}
|
|
9476
9476
|
});
|
|
9477
9477
|
|
|
9478
|
+
// ../core/src/managed-document-metadata.ts
|
|
9479
|
+
function isManagedDocumentMetadataField(field) {
|
|
9480
|
+
return MANAGED_DOCUMENT_METADATA_FIELDS.has(field);
|
|
9481
|
+
}
|
|
9482
|
+
var MANAGED_DOCUMENT_METADATA_FIELDS;
|
|
9483
|
+
var init_managed_document_metadata = __esm({
|
|
9484
|
+
"../core/src/managed-document-metadata.ts"() {
|
|
9485
|
+
"use strict";
|
|
9486
|
+
init_define_SUPERBEE_BUILD_IDENTITY();
|
|
9487
|
+
init_define_SUPERBEE_UPDATE_POLICY();
|
|
9488
|
+
MANAGED_DOCUMENT_METADATA_FIELDS = /* @__PURE__ */ new Set([
|
|
9489
|
+
"generated",
|
|
9490
|
+
"verified",
|
|
9491
|
+
"superbee_updated_by",
|
|
9492
|
+
"actor",
|
|
9493
|
+
"timestamp"
|
|
9494
|
+
]);
|
|
9495
|
+
}
|
|
9496
|
+
});
|
|
9497
|
+
|
|
9478
9498
|
// ../core/src/kinds.ts
|
|
9479
9499
|
function isConventionId(id) {
|
|
9480
9500
|
return id.startsWith(CONVENTIONS_PREFIX);
|
|
@@ -9616,18 +9636,32 @@ function projectKindForAuthoring(okfVersion, kind2) {
|
|
|
9616
9636
|
}
|
|
9617
9637
|
};
|
|
9618
9638
|
}
|
|
9639
|
+
function isKindFieldValidationWarning(warning) {
|
|
9640
|
+
return warning.code === "KIND_FIELD_MISSING" || warning.code === "KIND_FIELD_VALUE" || warning.code === "KIND_FIELD_ARITY";
|
|
9641
|
+
}
|
|
9642
|
+
function kindFieldWarningPrefix(code3, kind2, field) {
|
|
9643
|
+
switch (code3) {
|
|
9644
|
+
case "KIND_FIELD_MISSING":
|
|
9645
|
+
return `'${kind2.governs}' requires a non-empty '${field}' field `;
|
|
9646
|
+
case "KIND_FIELD_VALUE":
|
|
9647
|
+
return `'${field}' value `;
|
|
9648
|
+
case "KIND_FIELD_ARITY":
|
|
9649
|
+
return `'${field}' is enum-restricted and takes exactly ONE value for '${kind2.governs}'; `;
|
|
9650
|
+
}
|
|
9651
|
+
}
|
|
9619
9652
|
function projectKindValidationWarnings(okfVersion, kind2, warnings) {
|
|
9620
9653
|
const progress = progressStatusCoordinate(okfVersion, kind2);
|
|
9621
9654
|
if (!progress) return warnings;
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9655
|
+
return warnings.map((warning) => {
|
|
9656
|
+
if (!isKindFieldValidationWarning(warning) || warning.field !== progress.storageField) return warning;
|
|
9657
|
+
const storedPrefix = kindFieldWarningPrefix(warning.code, kind2, progress.storageField);
|
|
9658
|
+
const logicalPrefix = kindFieldWarningPrefix(warning.code, kind2, progress.logicalField);
|
|
9659
|
+
return {
|
|
9660
|
+
...warning,
|
|
9661
|
+
field: progress.logicalField,
|
|
9662
|
+
message: warning.message.startsWith(storedPrefix) ? logicalPrefix + warning.message.slice(storedPrefix.length) : warning.message
|
|
9663
|
+
};
|
|
9664
|
+
});
|
|
9631
9665
|
}
|
|
9632
9666
|
function isPlainObject2(value) {
|
|
9633
9667
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
@@ -10111,7 +10145,7 @@ function validateAgainstKind(doc2, kind2) {
|
|
|
10111
10145
|
if (!hasOwn2(fm, field) || !isPresent(fm[field])) {
|
|
10112
10146
|
warnings.push({
|
|
10113
10147
|
code: "KIND_FIELD_MISSING",
|
|
10114
|
-
message:
|
|
10148
|
+
message: kindFieldWarningPrefix("KIND_FIELD_MISSING", kind2, field) + `(declared by ${kind2.id}).`,
|
|
10115
10149
|
field,
|
|
10116
10150
|
severity: "warning"
|
|
10117
10151
|
});
|
|
@@ -10124,7 +10158,7 @@ function validateAgainstKind(doc2, kind2) {
|
|
|
10124
10158
|
if (Array.isArray(raw)) {
|
|
10125
10159
|
warnings.push({
|
|
10126
10160
|
code: "KIND_FIELD_ARITY",
|
|
10127
|
-
message:
|
|
10161
|
+
message: kindFieldWarningPrefix("KIND_FIELD_ARITY", kind2, field) + `got ${raw.length} (${raw.map((v2) => String(v2)).join(", ")}).`,
|
|
10128
10162
|
field,
|
|
10129
10163
|
severity: "warning"
|
|
10130
10164
|
});
|
|
@@ -10135,7 +10169,7 @@ function validateAgainstKind(doc2, kind2) {
|
|
|
10135
10169
|
if (!allowedStrs.includes(v2)) {
|
|
10136
10170
|
warnings.push({
|
|
10137
10171
|
code: "KIND_FIELD_VALUE",
|
|
10138
|
-
message:
|
|
10172
|
+
message: kindFieldWarningPrefix("KIND_FIELD_VALUE", kind2, field) + `'${v2}' is not one of the allowed values for '${kind2.governs}': ${allowedStrs.join(", ")}.`,
|
|
10139
10173
|
field,
|
|
10140
10174
|
severity: "warning"
|
|
10141
10175
|
});
|
|
@@ -10295,7 +10329,7 @@ function assertOrdinaryPatch(existing, proposed) {
|
|
|
10295
10329
|
}
|
|
10296
10330
|
}
|
|
10297
10331
|
function assertAssignmentName(field) {
|
|
10298
|
-
if (
|
|
10332
|
+
if (isManagedDocumentMetadataField(field)) throw new InvalidInputError(`'${field}' is managed metadata and cannot be assigned by a field action.`);
|
|
10299
10333
|
if (field === "tags" || field === "sources") throw new InvalidInputError(`'${field}' requires an explicit collection add/remove/replace-all action.`);
|
|
10300
10334
|
}
|
|
10301
10335
|
function prepareDocumentAssignments(existing, assignments) {
|
|
@@ -10369,7 +10403,7 @@ function prepareDocumentFieldAction(existing, action, context) {
|
|
|
10369
10403
|
...context.okfVersion === "0.2" ? v02 : [],
|
|
10370
10404
|
...kind2 ? kindInputFieldNames(context.okfVersion, kind2) : []
|
|
10371
10405
|
])].filter((field) => {
|
|
10372
|
-
if (
|
|
10406
|
+
if (isManagedDocumentMetadataField(field) || field === "tags" || field === "sources") return false;
|
|
10373
10407
|
if (context.okfVersion === "0.1" && (field === "stale_after" || field === "usage_window")) return false;
|
|
10374
10408
|
const storage = kind2 && resolveKindFieldCoordinate(context.okfVersion, kind2, field);
|
|
10375
10409
|
return !containsCollection(existing.frontmatter[storage?.storageField ?? field]);
|
|
@@ -10512,12 +10546,13 @@ function prepareDocumentFieldAction(existing, action, context) {
|
|
|
10512
10546
|
if (scope.outcome !== "unchanged" && context.okfVersion === "0.1") candidate.frontmatter.timestamp = (context.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
|
|
10513
10547
|
return { candidate, scope, storageField };
|
|
10514
10548
|
}
|
|
10515
|
-
var FieldActionError,
|
|
10549
|
+
var FieldActionError, standard, v02;
|
|
10516
10550
|
var init_document_field_actions = __esm({
|
|
10517
10551
|
"../core/src/document-field-actions.ts"() {
|
|
10518
10552
|
"use strict";
|
|
10519
10553
|
init_define_SUPERBEE_BUILD_IDENTITY();
|
|
10520
10554
|
init_define_SUPERBEE_UPDATE_POLICY();
|
|
10555
|
+
init_managed_document_metadata();
|
|
10521
10556
|
init_errors();
|
|
10522
10557
|
init_okf_authored_values();
|
|
10523
10558
|
init_kinds();
|
|
@@ -10530,7 +10565,6 @@ var init_document_field_actions = __esm({
|
|
|
10530
10565
|
this.details = details;
|
|
10531
10566
|
}
|
|
10532
10567
|
};
|
|
10533
|
-
managed = /* @__PURE__ */ new Set(["generated", "verified", "superbee_updated_by", "actor", "timestamp"]);
|
|
10534
10568
|
standard = /* @__PURE__ */ new Set(["title", "description", "type", "resource"]);
|
|
10535
10569
|
v02 = /* @__PURE__ */ new Set(["status", "stale_after", "usage_window"]);
|
|
10536
10570
|
}
|
|
@@ -10801,6 +10835,14 @@ function validateCandidate(id, candidate, registry2, strict, okfVersion, now, ex
|
|
|
10801
10835
|
}
|
|
10802
10836
|
return result3;
|
|
10803
10837
|
}
|
|
10838
|
+
function validateUnchangedDocument(existing, registry2, strict, okfVersion) {
|
|
10839
|
+
const kind2 = registry2.kinds.get(String(existing.frontmatter.type));
|
|
10840
|
+
const warnings = kind2 ? validateAgainstKind(existing, kind2) : [];
|
|
10841
|
+
if (strict && kind2 && warnings.length > 0) {
|
|
10842
|
+
throw new KindConformanceError(existing.id, kind2.governs, warnings, okfVersion);
|
|
10843
|
+
}
|
|
10844
|
+
return warnings;
|
|
10845
|
+
}
|
|
10804
10846
|
function withV02Metadata(candidate, existing, okfVersion, registry2, now, seedGenerationClock, actor, compareTimestamp, producer) {
|
|
10805
10847
|
if (okfVersion !== "0.2") return candidate;
|
|
10806
10848
|
const kind2 = registry2.kinds.get(String(candidate.frontmatter.type));
|
|
@@ -10848,7 +10890,7 @@ function prepareDocumentMutationCandidate(existing, rawCandidate, opts) {
|
|
|
10848
10890
|
opts.okfVersion === "0.2" && persistActor && opts.actor !== void 0,
|
|
10849
10891
|
opts.registry.kinds.get(String(comparison.frontmatter.type))?.fields.required.includes("actor") ?? false
|
|
10850
10892
|
)) {
|
|
10851
|
-
return { candidate: { frontmatter: structuredClone(existing.frontmatter), body: existing.body }, changed: false, warnings:
|
|
10893
|
+
return { candidate: { frontmatter: structuredClone(existing.frontmatter), body: existing.body }, changed: false, warnings: validateUnchangedDocument(existing, opts.registry, opts.strict, opts.okfVersion) };
|
|
10852
10894
|
}
|
|
10853
10895
|
const candidate = attributeCandidate(comparison, opts.actor, persistActor, opts.okfVersion, opts.registry);
|
|
10854
10896
|
const { warnings } = validateCandidate(opts.id, candidate, opts.registry, opts.strict, opts.okfVersion, decisionNow, existing);
|
|
@@ -10992,6 +11034,17 @@ async function mutateDocument(opts) {
|
|
|
10992
11034
|
okfVersion,
|
|
10993
11035
|
opts.registry
|
|
10994
11036
|
);
|
|
11037
|
+
if (existing && isNoopMutation(
|
|
11038
|
+
existing,
|
|
11039
|
+
candidate,
|
|
11040
|
+
compareTimestamp,
|
|
11041
|
+
okfVersion,
|
|
11042
|
+
persistActor && opts.actor !== void 0,
|
|
11043
|
+
opts.registry.kinds.get(String(candidate.frontmatter.type))?.fields.required.includes("actor") ?? false
|
|
11044
|
+
)) {
|
|
11045
|
+
warnings = validateUnchangedDocument(existing, opts.registry, opts.strict, okfVersion);
|
|
11046
|
+
return { action: "done", result: { doc: existing } };
|
|
11047
|
+
}
|
|
10995
11048
|
const validated = validateCandidate(
|
|
10996
11049
|
opts.id,
|
|
10997
11050
|
candidate,
|
|
@@ -11005,16 +11058,6 @@ async function mutateDocument(opts) {
|
|
|
11005
11058
|
if (!opts.strict && existing && warnings.length > 0 && conforms(existing, opts.registry)) {
|
|
11006
11059
|
throw new KindConformanceError(opts.id, validated.kind.governs, warnings, okfVersion);
|
|
11007
11060
|
}
|
|
11008
|
-
if (existing && isNoopMutation(
|
|
11009
|
-
existing,
|
|
11010
|
-
candidate,
|
|
11011
|
-
compareTimestamp,
|
|
11012
|
-
okfVersion,
|
|
11013
|
-
persistActor && opts.actor !== void 0,
|
|
11014
|
-
validated.kind?.fields.required.includes("actor") ?? false
|
|
11015
|
-
)) {
|
|
11016
|
-
return { action: "done", result: { doc: existing } };
|
|
11017
|
-
}
|
|
11018
11061
|
return { action: "write", next: { id: opts.id, ...candidate }, result: {} };
|
|
11019
11062
|
},
|
|
11020
11063
|
write: async (next, expectedVersion) => {
|
|
@@ -11056,7 +11099,7 @@ async function mutateDocument(opts) {
|
|
|
11056
11099
|
compareTimestamp,
|
|
11057
11100
|
seedGenerationClock: seedClock
|
|
11058
11101
|
});
|
|
11059
|
-
if (!changed) return { action: "done", result: { doc: existing, warnings
|
|
11102
|
+
if (!changed) return { action: "done", result: { doc: existing, warnings } };
|
|
11060
11103
|
return { action: "write", next: { id: opts.id, ...candidate }, result: { warnings } };
|
|
11061
11104
|
},
|
|
11062
11105
|
write: async (next, expectedVersion) => {
|
|
@@ -11070,7 +11113,7 @@ async function mutateDocument(opts) {
|
|
|
11070
11113
|
maxAttempts: hardCas ? 1 : maxAttempts
|
|
11071
11114
|
});
|
|
11072
11115
|
const scoped = { ...scope ? { scope } : {}, ...scopes ? { scopes } : {} };
|
|
11073
|
-
return outcome.wrote ? { doc: savedDoc, changed: true, version: outcome.version, warnings: outcome.result.warnings, ...scoped } : { doc: outcome.result.doc, changed: false, version: outcome.version, warnings:
|
|
11116
|
+
return outcome.wrote ? { doc: savedDoc, changed: true, version: outcome.version, warnings: outcome.result.warnings, ...scoped } : { doc: outcome.result.doc, changed: false, version: outcome.version, warnings: outcome.result.warnings, ...scoped };
|
|
11074
11117
|
}
|
|
11075
11118
|
var DEFAULT_MAX_ATTEMPTS, KindConformanceError, DocumentNotFoundError;
|
|
11076
11119
|
var init_document_mutation = __esm({
|
|
@@ -11727,6 +11770,7 @@ var init_src = __esm({
|
|
|
11727
11770
|
init_document_field_actions();
|
|
11728
11771
|
init_document_mutation();
|
|
11729
11772
|
init_kind_field_mutation();
|
|
11773
|
+
init_managed_document_metadata();
|
|
11730
11774
|
}
|
|
11731
11775
|
});
|
|
11732
11776
|
|
|
@@ -11749,6 +11793,16 @@ function hasRejectedReason(text5, reasons) {
|
|
|
11749
11793
|
}
|
|
11750
11794
|
return false;
|
|
11751
11795
|
}
|
|
11796
|
+
function hasLostRefLock(text5) {
|
|
11797
|
+
for (const line2 of text5.split("\n")) {
|
|
11798
|
+
const lower = line2.toLowerCase();
|
|
11799
|
+
const remote = lower.indexOf("[remote rejected]");
|
|
11800
|
+
if (remote !== -1 && lower.indexOf("(incorrect old value provided)", remote) !== -1) return true;
|
|
11801
|
+
const at = lower.indexOf("cannot lock ref");
|
|
11802
|
+
if (at !== -1 && lower.indexOf("but expected", at) !== -1) return true;
|
|
11803
|
+
}
|
|
11804
|
+
return false;
|
|
11805
|
+
}
|
|
11752
11806
|
function hasUnmergeableOriginRef(text5) {
|
|
11753
11807
|
const suffix = " - not something we can merge";
|
|
11754
11808
|
for (const line2 of text5.split("\n")) {
|
|
@@ -11790,7 +11844,7 @@ ${f2.stdout}`;
|
|
|
11790
11844
|
{ details: { op: op2, retryable: true } }
|
|
11791
11845
|
);
|
|
11792
11846
|
}
|
|
11793
|
-
if (op2 === "push" && (hasRejectedReason(text5, ["fetch first", "non-fast-forward"]) || /Updates were rejected because (?:the remote contains work|the tip of your current branch is behind)/i.test(text5))) {
|
|
11847
|
+
if (op2 === "push" && (hasRejectedReason(text5, ["fetch first", "non-fast-forward"]) || /Updates were rejected because (?:the remote contains work|the tip of your current branch is behind)/i.test(text5) || hasLostRefLock(text5))) {
|
|
11794
11848
|
return new BoardGitError(
|
|
11795
11849
|
"TRANSIENT",
|
|
11796
11850
|
"a teammate pushed to the board at the same time \u2014 re-run sync to incorporate their changes and retry",
|
|
@@ -11930,6 +11984,68 @@ import {
|
|
|
11930
11984
|
} from "node:fs";
|
|
11931
11985
|
import { tmpdir } from "node:os";
|
|
11932
11986
|
import path6 from "node:path";
|
|
11987
|
+
function isBoardBranchName(name) {
|
|
11988
|
+
return name === BOARD_BRANCH || name.length <= 100 && NAMED_BOARD_BRANCH.test(name) && !RESERVED_BOARD_LIKE_BRANCHES.has(name);
|
|
11989
|
+
}
|
|
11990
|
+
function declaredBoardBranchAtRef(dir, ref) {
|
|
11991
|
+
const entry = runGit(dir, ["ls-tree", "-z", ref, "--", BOARD_MARKER_FILE]);
|
|
11992
|
+
if (entry.status !== 0 || !/^100644 blob [0-9a-f]+\t\.superbee-board\.json\0$/.test(entry.stdout)) return null;
|
|
11993
|
+
const size = runGit(dir, ["cat-file", "-s", `${ref}:${BOARD_MARKER_FILE}`]);
|
|
11994
|
+
if (size.status !== 0 || Number(size.stdout.trim()) > BOARD_MARKER_BYTES) return null;
|
|
11995
|
+
const shown2 = runGit(dir, ["show", `${ref}:${BOARD_MARKER_FILE}`]);
|
|
11996
|
+
if (shown2.status !== 0) return null;
|
|
11997
|
+
try {
|
|
11998
|
+
const parsed2 = JSON.parse(shown2.stdout);
|
|
11999
|
+
if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2)) return null;
|
|
12000
|
+
const { schema, branch } = parsed2;
|
|
12001
|
+
if (schema !== BOARD_MARKER_SCHEMA || typeof branch !== "string") return null;
|
|
12002
|
+
return branch !== BOARD_BRANCH && isBoardBranchName(branch) ? branch : null;
|
|
12003
|
+
} catch {
|
|
12004
|
+
return null;
|
|
12005
|
+
}
|
|
12006
|
+
}
|
|
12007
|
+
function isDeclaredBoardBranch(dir, branch) {
|
|
12008
|
+
if (branch === BOARD_BRANCH) return declaredBoardBranchAtRef(dir, `refs/heads/${BOARD_BRANCH}`) === null;
|
|
12009
|
+
if (!isBoardBranchName(branch)) return false;
|
|
12010
|
+
return declaredBoardBranchAtRef(dir, `refs/heads/${branch}`) === branch;
|
|
12011
|
+
}
|
|
12012
|
+
function namedBoardRecoveryHelp(dir, branch) {
|
|
12013
|
+
const restore = `restore ${BOARD_MARKER_FILE} with {"schema": ${BOARD_MARKER_SCHEMA}, "branch": "${branch}"} in a new commit (git log -- ${BOARD_MARKER_FILE} shows who changed it)`;
|
|
12014
|
+
if (declaredBoardBranchAtRef(dir, `refs/remotes/${BOARD_REMOTE}/${branch}`) === branch) {
|
|
12015
|
+
return `${BOARD_REMOTE}/${branch} still declares the board: git pull --rebase ${BOARD_REMOTE} ${branch} restores ${BOARD_MARKER_FILE} here, then re-run sync`;
|
|
12016
|
+
}
|
|
12017
|
+
return `git fetch ${BOARD_REMOTE} ${branch}; if ${BOARD_REMOTE}/${branch} declares the board again, git pull --rebase ${BOARD_REMOTE} ${branch} restores the marker, otherwise ${restore}; then re-run sync`;
|
|
12018
|
+
}
|
|
12019
|
+
function rebaseStartBranch(dir) {
|
|
12020
|
+
for (const state of ["rebase-merge", "rebase-apply"]) {
|
|
12021
|
+
const headNamePath = path6.join(worktreeGitPath(dir, state), "head-name");
|
|
12022
|
+
if (!existsSync2(headNamePath)) continue;
|
|
12023
|
+
try {
|
|
12024
|
+
const name = readFileSync4(headNamePath, "utf8").trim();
|
|
12025
|
+
if (name.startsWith("refs/heads/")) return name.slice("refs/heads/".length);
|
|
12026
|
+
} catch {
|
|
12027
|
+
}
|
|
12028
|
+
}
|
|
12029
|
+
return null;
|
|
12030
|
+
}
|
|
12031
|
+
function boardBranchOf(boardPath) {
|
|
12032
|
+
const attached = runGit(boardPath, ["symbolic-ref", "-q", "--short", "HEAD"]);
|
|
12033
|
+
const name = attached.status === 0 ? attached.stdout.trim() : rebaseStartBranch(boardPath);
|
|
12034
|
+
if (name === null || name === BOARD_BRANCH || !isBoardBranchName(name)) return BOARD_BRANCH;
|
|
12035
|
+
if (isDeclaredBoardBranch(boardPath, name)) return name;
|
|
12036
|
+
if (!hasTrackedBundleRootAtRef(boardPath, `refs/heads/${name}`)) return BOARD_BRANCH;
|
|
12037
|
+
throw new BoardGitError(
|
|
12038
|
+
"CONFLICT",
|
|
12039
|
+
`this checkout is on the named board branch '${name}', but its tip no longer declares it: ${BOARD_MARKER_FILE} is missing or names another branch \u2014 sync will not guess which board to use`,
|
|
12040
|
+
{
|
|
12041
|
+
details: { path: boardPath, state: "named-board-undeclared", branch: name },
|
|
12042
|
+
help: namedBoardRecoveryHelp(boardPath, name)
|
|
12043
|
+
}
|
|
12044
|
+
);
|
|
12045
|
+
}
|
|
12046
|
+
function boardRefOf(boardPath) {
|
|
12047
|
+
return `${BOARD_REMOTE}/${boardBranchOf(boardPath)}`;
|
|
12048
|
+
}
|
|
11933
12049
|
function recognizedBundlePath(name, sourcePath) {
|
|
11934
12050
|
try {
|
|
11935
12051
|
const link4 = lstatSync(sourcePath);
|
|
@@ -12168,28 +12284,32 @@ function hasTrackedBundleRootAtRef(top, ref) {
|
|
|
12168
12284
|
function hasTrackedBundleRootAtHead(top) {
|
|
12169
12285
|
return hasTrackedBundleRootAtRef(top, "HEAD");
|
|
12170
12286
|
}
|
|
12171
|
-
function boardBranchTracksOrigin(top) {
|
|
12172
|
-
const remote = runGit(top, ["config", "--get", `branch.${
|
|
12173
|
-
const merge2 = runGit(top, ["config", "--get", `branch.${
|
|
12174
|
-
return remote.status === 0 && remote.stdout.trim() === BOARD_REMOTE && merge2.status === 0 && merge2.stdout.trim() === `refs/heads/${
|
|
12287
|
+
function boardBranchTracksOrigin(top, branch = BOARD_BRANCH) {
|
|
12288
|
+
const remote = runGit(top, ["config", "--get", `branch.${branch}.remote`]);
|
|
12289
|
+
const merge2 = runGit(top, ["config", "--get", `branch.${branch}.merge`]);
|
|
12290
|
+
return remote.status === 0 && remote.stdout.trim() === BOARD_REMOTE && merge2.status === 0 && merge2.stdout.trim() === `refs/heads/${branch}`;
|
|
12175
12291
|
}
|
|
12176
|
-
function standaloneHistoryBase(top) {
|
|
12177
|
-
const r2 = runGit(top, ["merge-base", `refs/heads/${
|
|
12292
|
+
function standaloneHistoryBase(top, branch = BOARD_BRANCH) {
|
|
12293
|
+
const r2 = runGit(top, ["merge-base", `refs/heads/${branch}`, `refs/remotes/${BOARD_REMOTE}/${branch}`]);
|
|
12178
12294
|
return r2.status === 0 && r2.stdout.trim().length > 0 ? r2.stdout.trim() : null;
|
|
12179
12295
|
}
|
|
12180
12296
|
function resolveStandaloneBoardCheckout(dir, opts = {}) {
|
|
12181
12297
|
const top = repoTopLevel(dir);
|
|
12182
|
-
if (!top
|
|
12183
|
-
|
|
12184
|
-
|
|
12298
|
+
if (!top) return null;
|
|
12299
|
+
const branch = currentBranch(top);
|
|
12300
|
+
if (!isDeclaredBoardBranch(top, branch)) return null;
|
|
12301
|
+
if (!hasTrackedBundleRootAtRef(top, `refs/heads/${branch}`) || !boardBranchTracksOrigin(top, branch)) return null;
|
|
12302
|
+
const remote = runGit(top, ["rev-parse", "--verify", "--quiet", `refs/remotes/${BOARD_REMOTE}/${branch}`]);
|
|
12185
12303
|
if (remote.status !== 0) return opts.requireRemoteRef === false ? top : null;
|
|
12186
|
-
return standaloneHistoryBase(top) ? top : null;
|
|
12304
|
+
return standaloneHistoryBase(top, branch) ? top : null;
|
|
12187
12305
|
}
|
|
12188
12306
|
function isRecoverableStandaloneBoardCheckout(dir) {
|
|
12189
12307
|
const top = repoTopLevel(dir);
|
|
12190
|
-
if (!top || !detectStaleRebase(top)
|
|
12191
|
-
|
|
12192
|
-
|
|
12308
|
+
if (!top || !detectStaleRebase(top)) return false;
|
|
12309
|
+
const branch = rebaseStartBranch(top);
|
|
12310
|
+
if (branch === null || !isDeclaredBoardBranch(top, branch)) return false;
|
|
12311
|
+
if (!hasTrackedBundleRootAtRef(top, `refs/heads/${branch}`) || !boardBranchTracksOrigin(top, branch)) return false;
|
|
12312
|
+
return standaloneHistoryBase(top, branch) !== null;
|
|
12193
12313
|
}
|
|
12194
12314
|
function resolveProvisionedBoardPath(dir, hostPolicy) {
|
|
12195
12315
|
const policy = captureBoardHostPolicy(hostPolicy);
|
|
@@ -12200,13 +12320,24 @@ function resolveProvisionedBoardPath(dir, hostPolicy) {
|
|
|
12200
12320
|
return conventionalProvisionedBoardPath(top, policy);
|
|
12201
12321
|
}
|
|
12202
12322
|
function standaloneRootWrongBranch(top, branch) {
|
|
12323
|
+
const stray = branch === BOARD_BRANCH ? declaredBoardBranchAtRef(top, `refs/heads/${BOARD_BRANCH}`) : null;
|
|
12324
|
+
if (stray !== null) {
|
|
12325
|
+
return new BoardGitError(
|
|
12326
|
+
"CONFLICT",
|
|
12327
|
+
`this '${BOARD_BRANCH}' checkout's tip carries ${BOARD_MARKER_FILE} declaring the named board '${stray}' \u2014 a named board's marker does not belong on the default board, so sync will not publish this tree`,
|
|
12328
|
+
{
|
|
12329
|
+
details: { path: top, state: "default-board-carries-named-marker", branch, declares: stray },
|
|
12330
|
+
help: `if this clone is the named board, rename it back (git branch -m ${stray} and track ${BOARD_REMOTE}/${stray}); otherwise remove the stray marker (git rm ${BOARD_MARKER_FILE} && git commit; git log -- ${BOARD_MARKER_FILE} shows who added it), then re-run sync`
|
|
12331
|
+
}
|
|
12332
|
+
);
|
|
12333
|
+
}
|
|
12203
12334
|
const shown2 = branch === "HEAD" ? "detached HEAD" : `'${branch}'`;
|
|
12204
12335
|
return new BoardGitError(
|
|
12205
12336
|
"CONFLICT",
|
|
12206
|
-
`this repository root is an OKF bundle, but it is checked out at ${shown2}; a standalone root checkout may sync only when it is attached to the dedicated '${BOARD_BRANCH}' branch`,
|
|
12337
|
+
`this repository root is an OKF bundle, but it is checked out at ${shown2}; a standalone root checkout may sync only when it is attached to the dedicated '${BOARD_BRANCH}' branch or to a named board branch (board-<name>) whose committed ${BOARD_MARKER_FILE} declares that name`,
|
|
12207
12338
|
{
|
|
12208
12339
|
details: { path: top, state: "standalone-board-wrong-branch", branch },
|
|
12209
|
-
help: `use a separate checkout attached to '${BOARD_BRANCH}', or run sync from the repository's code checkout and let it provision the conventional bundle worktree`
|
|
12340
|
+
help: branch !== BOARD_BRANCH && isBoardBranchName(branch) ? namedBoardRecoveryHelp(top, branch) : `use a separate checkout attached to '${BOARD_BRANCH}' (or to the named board branch), or run sync from the repository's code checkout and let it provision the conventional bundle worktree`
|
|
12210
12341
|
}
|
|
12211
12342
|
);
|
|
12212
12343
|
}
|
|
@@ -12439,11 +12570,13 @@ function provisionBoardWorktree(dir, budget = {}, hostPolicy) {
|
|
|
12439
12570
|
if (!top) return { kind: "no_repo" };
|
|
12440
12571
|
const rootIsBundle = hasTrackedBundleRootAtHead(top);
|
|
12441
12572
|
const rootBranch = rootIsBundle ? currentBranch(top) : void 0;
|
|
12442
|
-
if (rootBranch !== void 0 && rootBranch
|
|
12573
|
+
if (rootBranch !== void 0 && !isDeclaredBoardBranch(top, rootBranch)) {
|
|
12443
12574
|
throw standaloneRootWrongBranch(top, rootBranch);
|
|
12444
12575
|
}
|
|
12445
|
-
const
|
|
12446
|
-
const
|
|
12576
|
+
const branch = rootBranch ?? BOARD_BRANCH;
|
|
12577
|
+
const remoteRef = `refs/remotes/${BOARD_REMOTE}/${branch}`;
|
|
12578
|
+
const rootOriginBefore = rootBranch !== void 0 ? runGit(top, ["rev-parse", "--verify", "--quiet", remoteRef]) : null;
|
|
12579
|
+
const rootCheckout = rootBranch !== void 0 ? resolveStandaloneBoardCheckout(top, { requireRemoteRef: false }) : null;
|
|
12447
12580
|
const rootCachedBaseline = rootCheckout !== null && rootOriginBefore?.status === 0 ? rootOriginBefore.stdout.trim() : null;
|
|
12448
12581
|
const bundleDir = bundleDirNameForProject(top);
|
|
12449
12582
|
const boardPath = path6.join(top, bundleDir);
|
|
@@ -12475,13 +12608,13 @@ function provisionBoardWorktree(dir, budget = {}, hostPolicy) {
|
|
|
12475
12608
|
"ls-remote",
|
|
12476
12609
|
"--exit-code",
|
|
12477
12610
|
BOARD_REMOTE,
|
|
12478
|
-
`refs/heads/${
|
|
12611
|
+
`refs/heads/${branch}`
|
|
12479
12612
|
]);
|
|
12480
12613
|
if (probe?.status === 0) {
|
|
12481
12614
|
const children = runGit(top, [
|
|
12482
12615
|
"for-each-ref",
|
|
12483
12616
|
"--format=%(refname)",
|
|
12484
|
-
|
|
12617
|
+
`${remoteRef}/`
|
|
12485
12618
|
]);
|
|
12486
12619
|
let namespaceReady = children.status === 0;
|
|
12487
12620
|
for (const child of children.stdout.split("\n").filter(Boolean)) {
|
|
@@ -12493,7 +12626,7 @@ function provisionBoardWorktree(dir, budget = {}, hostPolicy) {
|
|
|
12493
12626
|
"--prune",
|
|
12494
12627
|
"--no-tags",
|
|
12495
12628
|
BOARD_REMOTE,
|
|
12496
|
-
`+refs/heads/${
|
|
12629
|
+
`+refs/heads/${branch}:${remoteRef}`
|
|
12497
12630
|
]);
|
|
12498
12631
|
remoteState = fetch2?.status === 0 ? "absent" : "unknown";
|
|
12499
12632
|
liveFetch = fetch2?.status === 0;
|
|
@@ -12502,23 +12635,23 @@ function provisionBoardWorktree(dir, budget = {}, hostPolicy) {
|
|
|
12502
12635
|
}
|
|
12503
12636
|
} else if (probe?.status === 2) {
|
|
12504
12637
|
remoteBoardKnownAbsent = true;
|
|
12505
|
-
runGit(top, ["update-ref", "-d",
|
|
12638
|
+
runGit(top, ["update-ref", "-d", remoteRef]);
|
|
12506
12639
|
} else {
|
|
12507
12640
|
remoteState = "unknown";
|
|
12508
12641
|
}
|
|
12509
12642
|
}
|
|
12510
|
-
const localBoard = runGit(top, ["rev-parse", "--verify", "--quiet", `refs/heads/${
|
|
12511
|
-
const remoteBoard = runGit(top, ["rev-parse", "--verify", "--quiet",
|
|
12643
|
+
const localBoard = runGit(top, ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]);
|
|
12644
|
+
const remoteBoard = runGit(top, ["rev-parse", "--verify", "--quiet", remoteRef]);
|
|
12512
12645
|
const hasLocal = localBoard.status === 0;
|
|
12513
12646
|
const hasRemote = remoteBoard.status === 0 && !remoteBoardKnownAbsent;
|
|
12514
|
-
if (rootIsBundle && rootBranch
|
|
12647
|
+
if (rootIsBundle && rootBranch !== void 0) {
|
|
12515
12648
|
if (remoteState === "unknown" && !liveFetch) {
|
|
12516
12649
|
return { kind: "no_board", remoteState: "unknown" };
|
|
12517
12650
|
}
|
|
12518
12651
|
const standaloneCheckout = hasRemote ? resolveStandaloneBoardCheckout(top) : null;
|
|
12519
12652
|
if (standaloneCheckout !== null) {
|
|
12520
12653
|
const fetchedOrigin = remoteBoard.stdout.trim();
|
|
12521
|
-
const originBaseline = rootCachedBaseline !== null ? rootCachedBaseline === fetchedOrigin ? null : rootCachedBaseline : standaloneHistoryBase(top);
|
|
12654
|
+
const originBaseline = rootCachedBaseline !== null ? rootCachedBaseline === fetchedOrigin ? null : rootCachedBaseline : standaloneHistoryBase(top, branch);
|
|
12522
12655
|
return originBaseline === null ? { kind: "already", boardPath: top } : { kind: "already", boardPath: top, originBaseline };
|
|
12523
12656
|
}
|
|
12524
12657
|
return { kind: "local_board", boardPath: top, remoteExists: hasRemote };
|
|
@@ -12953,13 +13086,14 @@ ${bodyLines2.join("\n")}
|
|
|
12953
13086
|
}
|
|
12954
13087
|
function fetchRebaseResolving(boardPath, exportDir) {
|
|
12955
13088
|
mustGit(boardPath, ["fetch", "--prune", BOARD_REMOTE], { timeoutMs: NETWORK_TIMEOUT_MS });
|
|
12956
|
-
|
|
13089
|
+
const boardRef = boardRefOf(boardPath);
|
|
13090
|
+
if (runGit(boardPath, ["rev-parse", "--verify", "--quiet", `refs/remotes/${boardRef}`]).status !== 0) {
|
|
12957
13091
|
return { status: "no_upstream" };
|
|
12958
13092
|
}
|
|
12959
13093
|
const idFlags = identityFlags(boardPath);
|
|
12960
|
-
const r2 = runGit(boardPath, [...idFlags, "rebase",
|
|
13094
|
+
const r2 = runGit(boardPath, [...idFlags, "rebase", boardRef], { rebase: true, timeoutMs: NETWORK_TIMEOUT_MS });
|
|
12961
13095
|
if (r2.status === 0) return { status: "clean" };
|
|
12962
|
-
if (!detectStaleRebase(boardPath)) throw classifyGitError(failureOf(["rebase",
|
|
13096
|
+
if (!detectStaleRebase(boardPath)) throw classifyGitError(failureOf(["rebase", boardRef], r2));
|
|
12963
13097
|
const listConflicted = () => mustGit(boardPath, ["diff", "--name-only", "-z", "--diff-filter=U"]).split("\0").filter((l) => l.length > 0);
|
|
12964
13098
|
const byPath = /* @__PURE__ */ new Map();
|
|
12965
13099
|
try {
|
|
@@ -12998,8 +13132,8 @@ function fetchRebaseResolving(boardPath, exportDir) {
|
|
|
12998
13132
|
}
|
|
12999
13133
|
}
|
|
13000
13134
|
}
|
|
13001
|
-
if (runGit(boardPath, ["cat-file", "-e", `refs/remotes/${
|
|
13002
|
-
mustGit(boardPath, ["checkout",
|
|
13135
|
+
if (runGit(boardPath, ["cat-file", "-e", `refs/remotes/${boardRef}:${relPath}`]).status === 0) {
|
|
13136
|
+
mustGit(boardPath, ["checkout", boardRef, "--", relPath]);
|
|
13003
13137
|
mustGit(boardPath, ["add", "--", relPath]);
|
|
13004
13138
|
} else {
|
|
13005
13139
|
mustGit(boardPath, ["rm", "-f", "--", relPath]);
|
|
@@ -13026,8 +13160,9 @@ function fetchRebaseResolving(boardPath, exportDir) {
|
|
|
13026
13160
|
}
|
|
13027
13161
|
return { status: "resolved", conflicts: [...byPath.values()] };
|
|
13028
13162
|
}
|
|
13029
|
-
function push(boardPath) {
|
|
13030
|
-
|
|
13163
|
+
function push(boardPath, commit, branch = boardBranchOf(boardPath)) {
|
|
13164
|
+
const refspec = commit === void 0 ? branch : `${commit}:refs/heads/${branch}`;
|
|
13165
|
+
mustGit(boardPath, ["push", BOARD_REMOTE, refspec], { timeoutMs: NETWORK_TIMEOUT_MS });
|
|
13031
13166
|
}
|
|
13032
13167
|
function pushBoardUpstream(top) {
|
|
13033
13168
|
mustGit(top, ["push", "-u", BOARD_REMOTE, BOARD_BRANCH], { timeoutMs: NETWORK_TIMEOUT_MS });
|
|
@@ -13121,7 +13256,7 @@ function ffPull(boardPath, budget = {}) {
|
|
|
13121
13256
|
if (fetched.status !== 0) {
|
|
13122
13257
|
fetchReason = swallowReason(classifyGitError(failureOf(["fetch", "--prune"], fetched)));
|
|
13123
13258
|
}
|
|
13124
|
-
const merged = runGit(boardPath, ["merge", "--ff-only",
|
|
13259
|
+
const merged = runGit(boardPath, ["merge", "--ff-only", boardRefOf(boardPath)]);
|
|
13125
13260
|
if (merged.status !== 0) {
|
|
13126
13261
|
const text5 = `${merged.stderr}
|
|
13127
13262
|
${merged.stdout}`;
|
|
@@ -13143,10 +13278,11 @@ ${merged.stdout}`;
|
|
|
13143
13278
|
}
|
|
13144
13279
|
}
|
|
13145
13280
|
function unpushedCount(boardPath) {
|
|
13146
|
-
|
|
13281
|
+
const boardRef = boardRefOf(boardPath);
|
|
13282
|
+
if (runGit(boardPath, ["rev-parse", "--verify", "--quiet", `refs/remotes/${boardRef}`]).status !== 0) {
|
|
13147
13283
|
return null;
|
|
13148
13284
|
}
|
|
13149
|
-
const out = mustGit(boardPath, ["rev-list", "--count", `${
|
|
13285
|
+
const out = mustGit(boardPath, ["rev-list", "--count", `${boardRef}..HEAD`]).trim();
|
|
13150
13286
|
const n = Number.parseInt(out, 10);
|
|
13151
13287
|
return Number.isFinite(n) ? n : 0;
|
|
13152
13288
|
}
|
|
@@ -13177,7 +13313,7 @@ function readDocBytesAtRef(dir, ref, relPath) {
|
|
|
13177
13313
|
}
|
|
13178
13314
|
return shown2.stdout;
|
|
13179
13315
|
}
|
|
13180
|
-
var BOARD_BRANCH, BOARD_REMOTE, BOARD_REF, BUNDLE_DIR, LEGACY_BUNDLE_DIR, BUNDLE_DIRS, RELATIVE_WORKTREE_CONFIG, SCRUBBED_GIT_VARS, LOCAL_TIMEOUT_MS, NETWORK_TIMEOUT_MS, IDENTITY_FALLBACK_ACTOR, IGNORABLE_BOARD_DIR_ENTRIES, UNKNOWN2, MAX_REBASE_STOPS, GITIGNORE_ENTRIES;
|
|
13316
|
+
var BOARD_BRANCH, BOARD_REMOTE, BOARD_REF, BOARD_MARKER_FILE, BOARD_MARKER_SCHEMA, BOARD_MARKER_BYTES, NAMED_BOARD_BRANCH, RESERVED_BOARD_LIKE_BRANCHES, BUNDLE_DIR, LEGACY_BUNDLE_DIR, BUNDLE_DIRS, RELATIVE_WORKTREE_CONFIG, SCRUBBED_GIT_VARS, LOCAL_TIMEOUT_MS, NETWORK_TIMEOUT_MS, IDENTITY_FALLBACK_ACTOR, IGNORABLE_BOARD_DIR_ENTRIES, UNKNOWN2, MAX_REBASE_STOPS, GITIGNORE_ENTRIES;
|
|
13181
13317
|
var init_porcelain = __esm({
|
|
13182
13318
|
"../board-git/src/porcelain.ts"() {
|
|
13183
13319
|
"use strict";
|
|
@@ -13190,6 +13326,11 @@ var init_porcelain = __esm({
|
|
|
13190
13326
|
BOARD_BRANCH = "board";
|
|
13191
13327
|
BOARD_REMOTE = "origin";
|
|
13192
13328
|
BOARD_REF = `${BOARD_REMOTE}/${BOARD_BRANCH}`;
|
|
13329
|
+
BOARD_MARKER_FILE = ".superbee-board.json";
|
|
13330
|
+
BOARD_MARKER_SCHEMA = 1;
|
|
13331
|
+
BOARD_MARKER_BYTES = 4 * 1024;
|
|
13332
|
+
NAMED_BOARD_BRANCH = /^board-[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
13333
|
+
RESERVED_BOARD_LIKE_BRANCHES = /* @__PURE__ */ new Set(["board-cleanup"]);
|
|
13193
13334
|
BUNDLE_DIR = ".superbee";
|
|
13194
13335
|
LEGACY_BUNDLE_DIR = ".agentstate-lite";
|
|
13195
13336
|
BUNDLE_DIRS = [BUNDLE_DIR, LEGACY_BUNDLE_DIR];
|
|
@@ -13223,7 +13364,7 @@ function localBranchExists(top, name) {
|
|
|
13223
13364
|
return runGit(top, ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`]).status === 0;
|
|
13224
13365
|
}
|
|
13225
13366
|
function resolveOriginRef(boardPath) {
|
|
13226
|
-
const r2 = runGit(boardPath, ["rev-parse", "--verify", "--quiet", `refs/remotes/${
|
|
13367
|
+
const r2 = runGit(boardPath, ["rev-parse", "--verify", "--quiet", `refs/remotes/${boardRefOf(boardPath)}`]);
|
|
13227
13368
|
return r2.status === 0 ? r2.stdout.trim() : null;
|
|
13228
13369
|
}
|
|
13229
13370
|
function hasLocalOnlyBundle(dir) {
|
|
@@ -13932,7 +14073,10 @@ function hasStandaloneBoardSignature(p2) {
|
|
|
13932
14073
|
try {
|
|
13933
14074
|
if (!statSync3(path10.join(p2, ".git")).isDirectory()) return false;
|
|
13934
14075
|
if (!statSync3(path10.join(p2, "index.md")).isFile()) return false;
|
|
13935
|
-
|
|
14076
|
+
const head = readFileSync6(path10.join(p2, ".git", "HEAD"), "utf8").trim();
|
|
14077
|
+
if (head === `ref: refs/heads/${BOARD_BRANCH}`) return true;
|
|
14078
|
+
const branch = head.startsWith("ref: refs/heads/") ? head.slice("ref: refs/heads/".length) : "";
|
|
14079
|
+
return isBoardBranchName(branch) && statSync3(path10.join(p2, BOARD_MARKER_FILE)).isFile();
|
|
13936
14080
|
} catch {
|
|
13937
14081
|
return false;
|
|
13938
14082
|
}
|
|
@@ -14386,6 +14530,7 @@ var init_errors4 = __esm({
|
|
|
14386
14530
|
NO_UPSTREAM: EXIT.RUNTIME,
|
|
14387
14531
|
GIT_BUSY: EXIT.RUNTIME,
|
|
14388
14532
|
CONFLICT: EXIT.CONFLICT,
|
|
14533
|
+
GATE_FAILED: EXIT.CONFLICT,
|
|
14389
14534
|
CREDENTIAL_STORE_UNAVAILABLE: EXIT.RUNTIME
|
|
14390
14535
|
};
|
|
14391
14536
|
CliError = class extends Error {
|
|
@@ -17189,7 +17334,7 @@ var init_command_spec = __esm({
|
|
|
17189
17334
|
id: "sync",
|
|
17190
17335
|
leaves: [publicLeaf("sync", "sync", zero, 22, DIR_SYNC_SURFACE)],
|
|
17191
17336
|
usage: "sync [--establish [--yes] | --pull-only | --show-incoming <id> [--out <file> | --body-out <file>] | --inspect --doc <id> [--out <file>] | --resolve keep|take|revise --doc <id> | --restore-deletes | --accept-deletes <token> | --take-host-deletions <token>] [--dir <path>] [--limit <n>]",
|
|
17192
|
-
summary: "Share the board branch with a remote \u2014 commits, pulls, and pushes (git tier; --pull-only skips commit+push). The remote repository must already exist: Superbee does not create it. After confirming the repository exists and origin/board does not, --establish is the separate explicit act that creates/pushes the board branch; it needs repository-specific push capability and branch-create policy clearance. If origin/board exists, plain `sync` joins it. Works both from a project's conventional bundle worktree and from a standalone clone whose tracked OKF root is attached to the shared board branch. A bundle folder already committed on the code branch is the same flag's hard case: preview first, --yes executes, and the folder's removal from the code branch rides a prepared side-branch commit you push and open as a PR. A bundle committed with code and NO board branch anywhere is the IN-TREE mode (read-side): full sync refuses (sharing rides your normal commit/push), --pull-only fetches the branch's tracking upstream and reports incoming board docs ('git pull' delivers them), and --establish converts to a dedicated board branch. A doc changed on both sides converges: teammate's version kept, yours exported; --show-incoming <id> (exclusive with --pull-only) prints the incoming version as of the last fetch; --inspect --doc <id> and --resolve keep|take|revise --doc <id> settle that saved conflict with the same verbs as a hosted checkout. Board-reading commands (list/doc read/status/home/link show) auto-run the ff-only pull when board state is >~5m stale \u2014 silent, bounded (~2s), never a push; SUPERBEE_NO_AUTOPULL=<any value, even 0> disables it. Hosted checkout (a folder made by `checkout`): sync instead sends each edited file as one whole document under your own access and refreshes documents changed on the host, with one row per document (committed, conflict, held, refused, unknown, paused; non-zero exit until all are committed). A change to one document and a host change to another both land; a document changed on both sides is never merged but comes back as a conflict, resolved with --inspect --doc <id> then --resolve keep|take|revise --doc <id> (a resolution is recorded, then sent by the next plain sync). A held mass delete is accepted only by the person, typing the count in their own terminal (--accept-deletes); --restore-deletes puts the files back; --take-host-deletions takes a mass deletion the pull refused."
|
|
17337
|
+
summary: "Share the board branch with a remote \u2014 commits, pulls, and pushes (git tier; --pull-only skips commit+push). The remote repository must already exist: Superbee does not create it. After confirming the repository exists and origin/board does not, --establish is the separate explicit act that creates/pushes the board branch; it needs repository-specific push capability and branch-create policy clearance. If origin/board exists, plain `sync` joins it. Works both from a project's conventional bundle worktree and from a standalone clone whose tracked OKF root is attached to the shared board branch; a named board-<name> branch that commits a .superbee-board.json declaring that name is a second board in the same repository, synced from its own standalone clone against origin/board-<name>. A bundle folder already committed on the code branch is the same flag's hard case: preview first, --yes executes, and the folder's removal from the code branch rides a prepared side-branch commit you push and open as a PR. A bundle committed with code and NO board branch anywhere is the IN-TREE mode (read-side): full sync refuses (sharing rides your normal commit/push), --pull-only fetches the branch's tracking upstream and reports incoming board docs ('git pull' delivers them), and --establish converts to a dedicated board branch. A doc changed on both sides converges: teammate's version kept, yours exported; --show-incoming <id> (exclusive with --pull-only) prints the incoming version as of the last fetch; --inspect --doc <id> and --resolve keep|take|revise --doc <id> settle that saved conflict with the same verbs as a hosted checkout. A clone may declare a pre-push gate with git config superbee.syncGate '<command>': it runs on the rebased tree before every push that sends commits, and a failure holds the push (work committed locally, exit 5, code GATE_FAILED). A push that loses a race to another writer re-fetches, rebases, re-gates and retries (up to 5 attempts) and is reported as a race, never as a permission problem. Board-reading commands (list/doc read/status/home/link show) auto-run the ff-only pull when board state is >~5m stale \u2014 silent, bounded (~2s), never a push; SUPERBEE_NO_AUTOPULL=<any value, even 0> disables it. Hosted checkout (a folder made by `checkout`): sync instead sends each edited file as one whole document under your own access and refreshes documents changed on the host, with one row per document (committed, conflict, held, refused, unknown, paused; non-zero exit until all are committed). A change to one document and a host change to another both land; a document changed on both sides is never merged but comes back as a conflict, resolved with --inspect --doc <id> then --resolve keep|take|revise --doc <id> (a resolution is recorded, then sent by the next plain sync). A held mass delete is accepted only by the person, typing the count in their own terminal (--accept-deletes); --restore-deletes puts the files back; --take-host-deletions takes a mass deletion the pull refused."
|
|
17193
17338
|
}
|
|
17194
17339
|
]
|
|
17195
17340
|
},
|
|
@@ -19191,7 +19336,9 @@ duplication \u2014 a plain overwrite that converges to the same on-disk state).
|
|
|
19191
19336
|
|
|
19192
19337
|
If a declared Kind governs the document type, prefer 'superbee new "<Kind>" <id> ...'; its
|
|
19193
19338
|
authoring is strict and create-only. Use 'doc write' for a generic type or a deliberate full
|
|
19194
|
-
replacement.
|
|
19339
|
+
replacement. Kind validation is advisory by default: violations appear as warnings, including on
|
|
19340
|
+
no-ops. --strict rejects violations without writing. A changed replacement of a previously conforming
|
|
19341
|
+
document cannot be nonconforming, even in advisory mode.
|
|
19195
19342
|
|
|
19196
19343
|
Options:
|
|
19197
19344
|
--type <t> OKF concept type (non-empty) [required]
|
|
@@ -19252,6 +19399,10 @@ Only the fields you pass change; everything else \u2014 including the body when
|
|
|
19252
19399
|
is preserved verbatim. Idempotent: a patch that changes NOTHING (ignoring the auto-refreshed
|
|
19253
19400
|
timestamp) converges to changed:false (no write, no timestamp refresh).
|
|
19254
19401
|
|
|
19402
|
+
Standard-field patches use advisory Kind validation by default, returning warnings even on no-ops;
|
|
19403
|
+
--strict rejects violations without writing. Dynamic declared fields are always strict: passing a
|
|
19404
|
+
--<field> flag requires the whole resulting document to satisfy its Kind, even for a same-value patch.
|
|
19405
|
+
|
|
19255
19406
|
Options:
|
|
19256
19407
|
--title <t> Replace the title
|
|
19257
19408
|
--description <d> Replace the description
|
|
@@ -19507,7 +19658,7 @@ function buildCompletingUpdateCommand(id, violations, kind2, okfVersion) {
|
|
|
19507
19658
|
const flags = [];
|
|
19508
19659
|
for (const violation of violations) {
|
|
19509
19660
|
const field = violation.field;
|
|
19510
|
-
if (!field || seen.has(field) || !declaredFields.has(field)) continue;
|
|
19661
|
+
if (!isKindFieldValidationWarning(violation) || !field || seen.has(field) || !declaredFields.has(field)) continue;
|
|
19511
19662
|
seen.add(field);
|
|
19512
19663
|
const allowed = kind2.fields.values[field];
|
|
19513
19664
|
const placeholder = allowed && allowed.length > 0 ? commandToken(`<${allowed.join("|")}>`) : commandLiteral("<value>");
|
|
@@ -19722,6 +19873,7 @@ async function translateMutationError(error51, opts) {
|
|
|
19722
19873
|
}
|
|
19723
19874
|
async function mutateDoc(opts) {
|
|
19724
19875
|
try {
|
|
19876
|
+
let okfVersion;
|
|
19725
19877
|
const result3 = await mutateDocument({
|
|
19726
19878
|
...opts.seedGenerationClock === void 0 ? {} : { seedGenerationClock: opts.seedGenerationClock },
|
|
19727
19879
|
bundle: opts.bundle,
|
|
@@ -19731,7 +19883,8 @@ async function mutateDoc(opts) {
|
|
|
19731
19883
|
strict: opts.strict,
|
|
19732
19884
|
buildCandidate: opts.buildCandidate,
|
|
19733
19885
|
input: opts.input,
|
|
19734
|
-
assertCandidate: (existing, candidate) => {
|
|
19886
|
+
assertCandidate: (existing, candidate, context) => {
|
|
19887
|
+
okfVersion = context.okfVersion;
|
|
19735
19888
|
if (existing) guardBodyReplace(opts.bundle, existing, candidate.body, opts.bodyReplace);
|
|
19736
19889
|
},
|
|
19737
19890
|
onAbsent: opts.onAbsent,
|
|
@@ -19742,7 +19895,8 @@ async function mutateDoc(opts) {
|
|
|
19742
19895
|
expectedVersion: opts.expectedVersion
|
|
19743
19896
|
});
|
|
19744
19897
|
if (result3.changed) await firePostPersist(opts.onPersisted);
|
|
19745
|
-
|
|
19898
|
+
const kind2 = opts.registry.kinds.get(String(result3.doc.frontmatter.type));
|
|
19899
|
+
return kind2 && result3.warnings.length > 0 ? { ...result3, warnings: projectKindValidationWarnings(okfVersion, kind2, result3.warnings) } : result3;
|
|
19746
19900
|
} catch (error51) {
|
|
19747
19901
|
return await translateMutationError(error51, opts);
|
|
19748
19902
|
}
|
|
@@ -29069,8 +29223,9 @@ async function gitBoardAt(canonicalRoot) {
|
|
|
29069
29223
|
if (top === null) return null;
|
|
29070
29224
|
if (top === canonicalRoot) {
|
|
29071
29225
|
const branch2 = gitText(top, ["symbolic-ref", "-q", "--short", "HEAD"]);
|
|
29072
|
-
if (branch2
|
|
29073
|
-
const
|
|
29226
|
+
if (branch2 === null || !isDeclaredBoardBranch(top, branch2)) return null;
|
|
29227
|
+
const boardRef = `${BOARD_REMOTE}/${branch2}`;
|
|
29228
|
+
const upstream2 = gitText(top, ["for-each-ref", "--format=%(upstream:short)", `refs/heads/${branch2}`]) ?? (gitText(top, ["rev-parse", "--verify", "--quiet", `refs/remotes/${boardRef}`]) === null ? null : boardRef);
|
|
29074
29229
|
return { channel: "branch", branch: branch2, upstream: upstream2, shared: upstream2 !== null, top, prefix: "" };
|
|
29075
29230
|
}
|
|
29076
29231
|
let committed;
|
|
@@ -30075,9 +30230,10 @@ async function loadClaimPolicy(boardPath) {
|
|
|
30075
30230
|
const bundle = configuredBundle(boardPath);
|
|
30076
30231
|
const [registry2, okfVersion] = await Promise.all([loadKinds(bundle), readBundleOkfVersion2(bundle)]);
|
|
30077
30232
|
if (registry2.kinds.size === 0) return INACTIVE_POLICY;
|
|
30233
|
+
const upstreamRef = boardRefOf(boardPath);
|
|
30078
30234
|
const resolved = /* @__PURE__ */ new Map();
|
|
30079
30235
|
return {
|
|
30080
|
-
provenance: `${
|
|
30236
|
+
provenance: `${upstreamRef}@${originSha}`,
|
|
30081
30237
|
forType(type) {
|
|
30082
30238
|
const name = typeof type === "string" ? type.trim() : "";
|
|
30083
30239
|
if (name === "") return void 0;
|
|
@@ -30088,7 +30244,7 @@ async function loadClaimPolicy(boardPath) {
|
|
|
30088
30244
|
return resolved.get(name);
|
|
30089
30245
|
},
|
|
30090
30246
|
upstreamFrontmatter(relPath) {
|
|
30091
|
-
const shown2 = runGit(boardPath, ["show", `${
|
|
30247
|
+
const shown2 = runGit(boardPath, ["show", `${upstreamRef}:${relPath}`]);
|
|
30092
30248
|
if (shown2.status !== 0) return void 0;
|
|
30093
30249
|
try {
|
|
30094
30250
|
return parseMarkdown(shown2.stdout, relPath).frontmatter;
|
|
@@ -30112,7 +30268,7 @@ function claimLostStatement(owner, provenance2) {
|
|
|
30112
30268
|
const who = owner === void 0 ? CLAIM_NO_OWNER_RECORDED : `owner is ${owner}`;
|
|
30113
30269
|
return `${who} as of ${provenance2}; ${CLAIM_NOT_ARBITRATED}`;
|
|
30114
30270
|
}
|
|
30115
|
-
var CLAIM_LOST_KEY, CLAIM_NOT_ARBITRATED, CLAIM_NO_OWNER_RECORDED,
|
|
30271
|
+
var CLAIM_LOST_KEY, CLAIM_NOT_ARBITRATED, CLAIM_NO_OWNER_RECORDED, ENGINE_STAMPED_FIELDS, INACTIVE_POLICY;
|
|
30116
30272
|
var init_claim_conflict = __esm({
|
|
30117
30273
|
"../cli/src/commands/sync/claim-conflict.ts"() {
|
|
30118
30274
|
"use strict";
|
|
@@ -30124,7 +30280,6 @@ var init_claim_conflict = __esm({
|
|
|
30124
30280
|
CLAIM_LOST_KEY = "claim_lost";
|
|
30125
30281
|
CLAIM_NOT_ARBITRATED = "your claim was not arbitrated";
|
|
30126
30282
|
CLAIM_NO_OWNER_RECORDED = "no owner is recorded";
|
|
30127
|
-
BOARD_UPSTREAM_REF = `${BOARD_REMOTE}/${BOARD_BRANCH}`;
|
|
30128
30283
|
ENGINE_STAMPED_FIELDS = /* @__PURE__ */ new Set([
|
|
30129
30284
|
"timestamp",
|
|
30130
30285
|
"generated",
|
|
@@ -30465,11 +30620,12 @@ async function showIncoming(id, values, deps, route) {
|
|
|
30465
30620
|
if (path27.isAbsolute(id) || id.split("/").some((seg) => seg === "..")) {
|
|
30466
30621
|
throw new CliError("USAGE", `--show-incoming needs a repo-relative doc id or path without '..' segments: ${id}`);
|
|
30467
30622
|
}
|
|
30468
|
-
|
|
30623
|
+
const boardBranch = boardBranchOf(top);
|
|
30624
|
+
let readRef = `refs/remotes/${BOARD_REMOTE}/${boardBranch}`;
|
|
30469
30625
|
let pathPrefix = "";
|
|
30470
30626
|
let inTreeBundleDir = bundleDirNameForProject(top);
|
|
30471
|
-
if (runGit(top, ["rev-parse", "--verify", "--quiet",
|
|
30472
|
-
if (runGit(top, ["rev-parse", "--verify", "--quiet", `refs/heads/${
|
|
30627
|
+
if (runGit(top, ["rev-parse", "--verify", "--quiet", readRef]).status !== 0) {
|
|
30628
|
+
if (runGit(top, ["rev-parse", "--verify", "--quiet", `refs/heads/${boardBranch}`]).status === 0) {
|
|
30473
30629
|
throw ffSwallowToError("no-upstream", inv, top);
|
|
30474
30630
|
}
|
|
30475
30631
|
const committed = committedBundleAtHead(top);
|
|
@@ -30773,7 +30929,7 @@ async function locateBoard(args, cwd) {
|
|
|
30773
30929
|
throw new CliError("CONFLICT", "the selected private board has a board-origin rebase pending; run sync first to recover it", { help: `${inv} sync${dirSuffix(args)}` });
|
|
30774
30930
|
}
|
|
30775
30931
|
const root = route.kind === "bound-board" ? route.owner.bundleRoot : route.bundle.root;
|
|
30776
|
-
if (repoTopLevel(root) === null || runGit(root, ["rev-parse", "--verify", "--quiet", `refs/remotes/${
|
|
30932
|
+
if (repoTopLevel(root) === null || runGit(root, ["rev-parse", "--verify", "--quiet", `refs/remotes/${boardRefOf(root)}`]).status !== 0) {
|
|
30777
30933
|
throw new CliError("USAGE", "--inspect and --resolve apply to a shared Git board or a hosted checkout; this bundle is neither, so sync has no conflicts to show", {
|
|
30778
30934
|
details: { bundle: root },
|
|
30779
30935
|
help: `${inv} sync --help`
|
|
@@ -30842,7 +30998,7 @@ function commandFor(args, choice) {
|
|
|
30842
30998
|
}
|
|
30843
30999
|
async function runInspect(args, board, cwd) {
|
|
30844
31000
|
const local = await fs10.readFile(board.exportPath);
|
|
30845
|
-
const remote = readDocBytesAtRef(board.root, `refs/remotes/${
|
|
31001
|
+
const remote = readDocBytesAtRef(board.root, `refs/remotes/${boardRefOf(board.root)}`, board.relPath);
|
|
30846
31002
|
if (args.out !== void 0) {
|
|
30847
31003
|
if (remote === null) throw new CliError("NOT_FOUND", `the teammate's side has no version of '${args.id}' to write: it was deleted there`, { details: { id: args.id } });
|
|
30848
31004
|
const outHelp = `${cliInvocation()} sync --inspect --doc ${commandToken(args.id)} --out <file outside the bundle>`;
|
|
@@ -30885,7 +31041,7 @@ async function runInspect(args, board, cwd) {
|
|
|
30885
31041
|
file: board.file,
|
|
30886
31042
|
reason: deleted ? "deleted_remotely" : "changed_remotely",
|
|
30887
31043
|
local: { version: versionOfBytes(localText), saved_at: board.exportPath, ...preview(localText) },
|
|
30888
|
-
remote: { version: remoteText === null ? null : versionOfBytes(remoteText), ref: `${
|
|
31044
|
+
remote: { version: remoteText === null ? null : versionOfBytes(remoteText), ref: `${boardRefOf(board.root)} (as of the last fetch)`, ...deleted ? { deleted: true } : {}, ...preview(remoteText) },
|
|
30889
31045
|
...claimFields(divergence),
|
|
30890
31046
|
...differs.length > 0 ? { frontmatter_differs: differs } : {},
|
|
30891
31047
|
...args.out !== void 0 ? { remote_written_to: path28.resolve(cwd, args.out) } : {},
|
|
@@ -30919,7 +31075,7 @@ async function discardSavedCopy(board) {
|
|
|
30919
31075
|
}
|
|
30920
31076
|
async function runResolve(args, choice, board, cwd) {
|
|
30921
31077
|
const inv = cliInvocation();
|
|
30922
|
-
const remote = readDocBytesAtRef(board.root, `refs/remotes/${
|
|
31078
|
+
const remote = readDocBytesAtRef(board.root, `refs/remotes/${boardRefOf(board.root)}`, board.relPath);
|
|
30923
31079
|
const bundleDir = conflictBundleDir(args.dir, cwd);
|
|
30924
31080
|
const dirArgs = bundleDir !== void 0 ? ["--dir", bundleDir] : [];
|
|
30925
31081
|
const local = await fs10.readFile(board.exportPath);
|
|
@@ -30965,7 +31121,7 @@ async function runResolve(args, choice, board, cwd) {
|
|
|
30965
31121
|
} else if (present !== null && present.equals(remote)) {
|
|
30966
31122
|
fileState = "unchanged";
|
|
30967
31123
|
} else {
|
|
30968
|
-
const restored = runGit(board.root, ["restore", `--source=refs/remotes/${
|
|
31124
|
+
const restored = runGit(board.root, ["restore", `--source=refs/remotes/${boardRefOf(board.root)}`, "--worktree", "--", board.relPath]);
|
|
30969
31125
|
if (restored.status !== 0) {
|
|
30970
31126
|
throw new CliError("RUNTIME", `could not restore the teammate's version of '${args.id}'; your saved copy was kept`, {
|
|
30971
31127
|
details: { id: args.id, git: restored.stderr.trim() },
|
|
@@ -32252,6 +32408,7 @@ __export(sync_exports, {
|
|
|
32252
32408
|
SYNC_IN_TREE_CURRENT: () => SYNC_IN_TREE_CURRENT,
|
|
32253
32409
|
SYNC_IN_TREE_NO_BASIS: () => SYNC_IN_TREE_NO_BASIS,
|
|
32254
32410
|
SYNC_LOCAL_ONLY_MESSAGE: () => SYNC_LOCAL_ONLY_MESSAGE,
|
|
32411
|
+
SYNC_PUSH_ATTEMPTS: () => SYNC_PUSH_ATTEMPTS,
|
|
32255
32412
|
SYNC_REMOTE_STATE_UNKNOWN_MESSAGE: () => SYNC_REMOTE_STATE_UNKNOWN_MESSAGE,
|
|
32256
32413
|
SYNC_USAGE: () => SYNC_USAGE,
|
|
32257
32414
|
buildConvergeError: () => buildConvergeError,
|
|
@@ -32314,7 +32471,11 @@ ${HOSTED_SYNC_USAGE}`));
|
|
|
32314
32471
|
}
|
|
32315
32472
|
if (requestsHostedOnlyVerb(argv2)) throw hostedOnlyVerbError(await targetHome(argv2, deps.cwd ?? process.cwd(), home2), argv2);
|
|
32316
32473
|
if (requestsConflictVerb(argv2)) {
|
|
32317
|
-
|
|
32474
|
+
try {
|
|
32475
|
+
await gitConflictVerb(argv2, deps);
|
|
32476
|
+
} catch (err) {
|
|
32477
|
+
throw isBoardGitError(err) ? cliErrorFromBoardGit(err) : err;
|
|
32478
|
+
}
|
|
32318
32479
|
return;
|
|
32319
32480
|
}
|
|
32320
32481
|
await sync(argv2, deps);
|
|
@@ -32346,6 +32507,7 @@ var init_sync = __esm({
|
|
|
32346
32507
|
init_converge();
|
|
32347
32508
|
init_show_incoming();
|
|
32348
32509
|
init_sync_outcomes();
|
|
32510
|
+
init_src2();
|
|
32349
32511
|
init_errors4();
|
|
32350
32512
|
init_output2();
|
|
32351
32513
|
init_sync2();
|
|
@@ -32654,7 +32816,7 @@ async function gitTurnEnd(dir, home2, partial2, stopHookActive) {
|
|
|
32654
32816
|
await run([...dir === void 0 ? [] : ["--dir", dir], "--limit", String(REASON_ROWS), "--json"], () => {
|
|
32655
32817
|
});
|
|
32656
32818
|
} catch (error51) {
|
|
32657
|
-
if (error51 instanceof CliError && (error51.code === "CONFLICT" || error51.code === "AUTH_REQUIRED" || error51.details?.reason === "board_moved")) blocking = error51;
|
|
32819
|
+
if (error51 instanceof CliError && (error51.code === "CONFLICT" || error51.code === "GATE_FAILED" || error51.code === "AUTH_REQUIRED" || error51.details?.reason === "board_moved")) blocking = error51;
|
|
32658
32820
|
}
|
|
32659
32821
|
if (!blocking) {
|
|
32660
32822
|
if (await readGitBlock(home2, board.root) !== null) await recordGitBlock(home2, board.root, null).catch(() => {
|
|
@@ -34530,6 +34692,9 @@ async function establishBoard(dir, inv, mode, stdout, deps, opts = {}) {
|
|
|
34530
34692
|
throw syncOutcomeError("establish.origin-unconfigured", { inv });
|
|
34531
34693
|
}
|
|
34532
34694
|
assertNotLinkedWorktree(top, inv);
|
|
34695
|
+
if (currentBranch(top) !== BOARD_BRANCH && resolveStandaloneBoardCheckout(top, { requireRemoteRef: false }) !== null) {
|
|
34696
|
+
return { already: true };
|
|
34697
|
+
}
|
|
34533
34698
|
const committed = committedBundleAtHead(top);
|
|
34534
34699
|
if (committed !== null) {
|
|
34535
34700
|
assertBundleOutsidePrivateState(path31.join(top, committed.bundleDir));
|
|
@@ -34598,6 +34763,114 @@ var init_establish = __esm({
|
|
|
34598
34763
|
}
|
|
34599
34764
|
});
|
|
34600
34765
|
|
|
34766
|
+
// ../cli/src/commands/sync/gate.ts
|
|
34767
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
34768
|
+
function declaredSyncGate(boardPath) {
|
|
34769
|
+
const configured = runGit(boardPath, ["config", "--get", SYNC_GATE_CONFIG]);
|
|
34770
|
+
const command = configured.status === 0 ? configured.stdout.trim() : "";
|
|
34771
|
+
if (command === "") return null;
|
|
34772
|
+
const timeout = runGit(boardPath, ["config", "--get", SYNC_GATE_TIMEOUT_CONFIG]);
|
|
34773
|
+
const raw = timeout.status === 0 ? timeout.stdout.trim() : "";
|
|
34774
|
+
const seconds = /^\d+$/.test(raw) && Number(raw) > 0 ? Number(raw) : DEFAULT_SYNC_GATE_TIMEOUT_SECONDS;
|
|
34775
|
+
return { command, timeoutSeconds: seconds };
|
|
34776
|
+
}
|
|
34777
|
+
async function runSyncGate(boardPath, branch, gate, attempt) {
|
|
34778
|
+
const head = currentHead(boardPath);
|
|
34779
|
+
const upstreamSha = resolveOriginRef(boardPath) ?? "";
|
|
34780
|
+
const base = {
|
|
34781
|
+
command: gate.command,
|
|
34782
|
+
attempt,
|
|
34783
|
+
head,
|
|
34784
|
+
upstream: `origin/${branch}`,
|
|
34785
|
+
...upstreamSha ? { upstream_sha: upstreamSha } : {}
|
|
34786
|
+
};
|
|
34787
|
+
const result3 = await runInOwnGroup(gate.command, boardPath, gate.timeoutSeconds * 1e3, {
|
|
34788
|
+
...gateBaseEnv(),
|
|
34789
|
+
SUPERBEE_SYNC_GATE: "1",
|
|
34790
|
+
SUPERBEE_BOARD_PATH: boardPath,
|
|
34791
|
+
SUPERBEE_BOARD_BRANCH: branch,
|
|
34792
|
+
SUPERBEE_BOARD_UPSTREAM_REF: `origin/${branch}`,
|
|
34793
|
+
SUPERBEE_BOARD_UPSTREAM_SHA: upstreamSha,
|
|
34794
|
+
SUPERBEE_BOARD_HEAD_SHA: head,
|
|
34795
|
+
SUPERBEE_SYNC_ATTEMPT: String(attempt)
|
|
34796
|
+
});
|
|
34797
|
+
const tail = outputTail(result3.output);
|
|
34798
|
+
const failed = (reason, extra) => ({
|
|
34799
|
+
passed: false,
|
|
34800
|
+
reason,
|
|
34801
|
+
details: { ...base, ...extra, ...tail ? { output_tail: tail } : {} }
|
|
34802
|
+
});
|
|
34803
|
+
if (result3.timedOut) {
|
|
34804
|
+
return failed(`timed out after ${gate.timeoutSeconds}s`, { timed_out: true, timeout_seconds: gate.timeoutSeconds });
|
|
34805
|
+
}
|
|
34806
|
+
if (result3.spawnError) return failed(`could not run (${result3.spawnError})`, { spawn_error: result3.spawnError });
|
|
34807
|
+
if (result3.signal) return failed(`was killed by ${result3.signal}`, { signal: result3.signal });
|
|
34808
|
+
if (result3.status !== 0) return failed(`exited ${result3.status}`, { exit_status: result3.status });
|
|
34809
|
+
const attached = runGit(boardPath, ["symbolic-ref", "-q", "--short", "HEAD"]);
|
|
34810
|
+
if (runGit(boardPath, ["rev-parse", "HEAD"]).stdout.trim() !== head || attached.status !== 0 || attached.stdout.trim() !== branch || countUncommitted(boardPath) > 0) {
|
|
34811
|
+
return failed("changed the board (a gate must only inspect it)", { modified_board: true });
|
|
34812
|
+
}
|
|
34813
|
+
return { passed: true, details: { ...base, exit_status: 0 } };
|
|
34814
|
+
}
|
|
34815
|
+
function runInOwnGroup(command, cwd, timeoutMs, env) {
|
|
34816
|
+
return new Promise((resolve2) => {
|
|
34817
|
+
let output = "";
|
|
34818
|
+
let timedOut = false;
|
|
34819
|
+
let settled = false;
|
|
34820
|
+
const child = spawn4(command, { cwd, env, shell: true, detached: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
34821
|
+
const killGroup = () => {
|
|
34822
|
+
if (child.pid === void 0) return;
|
|
34823
|
+
try {
|
|
34824
|
+
process.kill(-child.pid, "SIGKILL");
|
|
34825
|
+
} catch {
|
|
34826
|
+
}
|
|
34827
|
+
};
|
|
34828
|
+
const collect = (chunk) => {
|
|
34829
|
+
output += chunk.toString("utf8");
|
|
34830
|
+
if (output.length > OUTPUT_MAX_CHARS) output = output.slice(-OUTPUT_MAX_CHARS);
|
|
34831
|
+
};
|
|
34832
|
+
child.stdout?.on("data", collect);
|
|
34833
|
+
child.stderr?.on("data", collect);
|
|
34834
|
+
const timer = setTimeout(() => {
|
|
34835
|
+
timedOut = true;
|
|
34836
|
+
killGroup();
|
|
34837
|
+
}, timeoutMs);
|
|
34838
|
+
const finish = (run) => {
|
|
34839
|
+
if (settled) return;
|
|
34840
|
+
settled = true;
|
|
34841
|
+
clearTimeout(timer);
|
|
34842
|
+
killGroup();
|
|
34843
|
+
resolve2({ ...run, output, timedOut });
|
|
34844
|
+
};
|
|
34845
|
+
child.on("error", (error51) => finish({ status: null, signal: null, spawnError: error51.code ?? error51.message }));
|
|
34846
|
+
child.on("close", (status2, signal) => finish({ status: status2, signal }));
|
|
34847
|
+
});
|
|
34848
|
+
}
|
|
34849
|
+
function gateBaseEnv() {
|
|
34850
|
+
const env = { ...process.env };
|
|
34851
|
+
for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]) delete env[name];
|
|
34852
|
+
return env;
|
|
34853
|
+
}
|
|
34854
|
+
function outputTail(output) {
|
|
34855
|
+
const lines = output.replace(/\s+$/, "").split("\n");
|
|
34856
|
+
if (lines.length === 1 && lines[0] === "") return "";
|
|
34857
|
+
return lines.slice(-OUTPUT_TAIL_LINES).join("\n");
|
|
34858
|
+
}
|
|
34859
|
+
var SYNC_GATE_CONFIG, SYNC_GATE_TIMEOUT_CONFIG, DEFAULT_SYNC_GATE_TIMEOUT_SECONDS, OUTPUT_TAIL_LINES, OUTPUT_MAX_CHARS;
|
|
34860
|
+
var init_gate = __esm({
|
|
34861
|
+
"../cli/src/commands/sync/gate.ts"() {
|
|
34862
|
+
"use strict";
|
|
34863
|
+
init_define_SUPERBEE_BUILD_IDENTITY();
|
|
34864
|
+
init_define_SUPERBEE_UPDATE_POLICY();
|
|
34865
|
+
init_src2();
|
|
34866
|
+
SYNC_GATE_CONFIG = "superbee.syncGate";
|
|
34867
|
+
SYNC_GATE_TIMEOUT_CONFIG = "superbee.syncGateTimeoutSeconds";
|
|
34868
|
+
DEFAULT_SYNC_GATE_TIMEOUT_SECONDS = 600;
|
|
34869
|
+
OUTPUT_TAIL_LINES = 40;
|
|
34870
|
+
OUTPUT_MAX_CHARS = 256 * 1024;
|
|
34871
|
+
}
|
|
34872
|
+
});
|
|
34873
|
+
|
|
34601
34874
|
// ../cli/src/bound-board-recovery.ts
|
|
34602
34875
|
function recoveryFailure(target3, stage, message) {
|
|
34603
34876
|
return new CliError("CONFLICT", `project binding cannot recover its private board: ${message}`, {
|
|
@@ -34799,6 +35072,7 @@ __export(orchestrate_exports, {
|
|
|
34799
35072
|
SYNC_IN_TREE_CURRENT: () => SYNC_IN_TREE_CURRENT,
|
|
34800
35073
|
SYNC_IN_TREE_NO_BASIS: () => SYNC_IN_TREE_NO_BASIS,
|
|
34801
35074
|
SYNC_LOCAL_ONLY_MESSAGE: () => SYNC_LOCAL_ONLY_MESSAGE,
|
|
35075
|
+
SYNC_PUSH_ATTEMPTS: () => SYNC_PUSH_ATTEMPTS,
|
|
34802
35076
|
SYNC_REMOTE_STATE_UNKNOWN_MESSAGE: () => SYNC_REMOTE_STATE_UNKNOWN_MESSAGE,
|
|
34803
35077
|
SYNC_USAGE: () => SYNC_USAGE,
|
|
34804
35078
|
inTreePullHint: () => inTreePullHint,
|
|
@@ -35109,7 +35383,7 @@ async function commitPhase(board, pullOnly2) {
|
|
|
35109
35383
|
return commitResult;
|
|
35110
35384
|
}
|
|
35111
35385
|
async function pullPhase(run, board, commitResult) {
|
|
35112
|
-
const { boardPath,
|
|
35386
|
+
const { boardPath, outcome } = board;
|
|
35113
35387
|
if (run.pullOnly) {
|
|
35114
35388
|
const ff = ffPull(boardPath);
|
|
35115
35389
|
if (ff.swallowed) {
|
|
@@ -35117,7 +35391,11 @@ async function pullPhase(run, board, commitResult) {
|
|
|
35117
35391
|
}
|
|
35118
35392
|
return;
|
|
35119
35393
|
}
|
|
35120
|
-
|
|
35394
|
+
await convergingRebase(run, board, commitResult.committed);
|
|
35395
|
+
}
|
|
35396
|
+
async function convergingRebase(run, board, committedThisRun) {
|
|
35397
|
+
const { boardPath, key: key2, outcome } = board;
|
|
35398
|
+
const fail4 = (err) => throwPostCommitFailure(withProvisionAnnouncement(err, outcome), committedThisRun, key2, boardPath);
|
|
35121
35399
|
let rebaseOutcome;
|
|
35122
35400
|
try {
|
|
35123
35401
|
rebaseOutcome = fetchRebaseResolving(boardPath, defaultSyncStore.exportsDir(key2));
|
|
@@ -35150,29 +35428,94 @@ async function deltaPhase(board, baseline) {
|
|
|
35150
35428
|
);
|
|
35151
35429
|
return { originDelta, changes: [], reanchorNote: REANCHOR_NOTE };
|
|
35152
35430
|
}
|
|
35153
|
-
|
|
35154
|
-
|
|
35155
|
-
|
|
35156
|
-
|
|
35157
|
-
|
|
35158
|
-
|
|
35159
|
-
|
|
35160
|
-
|
|
35161
|
-
|
|
35162
|
-
const
|
|
35163
|
-
const
|
|
35164
|
-
|
|
35165
|
-
|
|
35166
|
-
|
|
35167
|
-
|
|
35168
|
-
run.
|
|
35169
|
-
|
|
35170
|
-
|
|
35171
|
-
|
|
35172
|
-
|
|
35173
|
-
|
|
35174
|
-
|
|
35175
|
-
|
|
35431
|
+
function raceBackoffMs(attempt) {
|
|
35432
|
+
return Math.floor(250 * attempt + Math.random() * 750 * attempt);
|
|
35433
|
+
}
|
|
35434
|
+
async function pushPhase(run, board, commitResult, delta, baseline) {
|
|
35435
|
+
if (run.pullOnly) return { commits: 0, documents: 0, delta };
|
|
35436
|
+
const gate = declaredSyncGate(board.boardPath);
|
|
35437
|
+
const branch = boardBranchOf(board.boardPath);
|
|
35438
|
+
let gateDetails;
|
|
35439
|
+
for (let attempt = 1; ; attempt += 1) {
|
|
35440
|
+
const head = currentHead(board.boardPath);
|
|
35441
|
+
const counted = unpushedCount(board.boardPath);
|
|
35442
|
+
const ahead = counted ?? 0;
|
|
35443
|
+
const outgoing = ahead > 0 ? new Set(originDocsBetween(board.boardPath, resolveOriginRef(board.boardPath), head).map((change) => change.docId)).size : 0;
|
|
35444
|
+
if (gate && (counted === null || ahead > 0)) {
|
|
35445
|
+
const judged = await runSyncGate(board.boardPath, branch, gate, attempt);
|
|
35446
|
+
if (!judged.passed) await failGate(run, board, commitResult, delta, judged.reason ?? "failed", judged.details);
|
|
35447
|
+
gateDetails = judged.details;
|
|
35448
|
+
}
|
|
35449
|
+
try {
|
|
35450
|
+
push(board.boardPath, head, branch);
|
|
35451
|
+
return { commits: ahead, documents: outgoing, delta, ...gateDetails ? { gate: gateDetails } : {} };
|
|
35452
|
+
} catch (err) {
|
|
35453
|
+
const classified = withSharingDetails(toCliError(err, "push"), { operation: "update-board" });
|
|
35454
|
+
const raced = classified.code === "TRANSIENT" && classified.details?.reason === "non-fast-forward";
|
|
35455
|
+
if (raced && attempt < SYNC_PUSH_ATTEMPTS) {
|
|
35456
|
+
await new Promise((resolve2) => setTimeout(resolve2, raceBackoffMs(attempt)));
|
|
35457
|
+
if (boardBranchOf(board.boardPath) !== branch) {
|
|
35458
|
+
throw new CliError("CONFLICT", `the board checkout left '${branch}' during sync; nothing more was pushed`, {
|
|
35459
|
+
details: { state: "board-branch-moved", branch },
|
|
35460
|
+
help: `check out '${branch}' in ${board.boardPath}, then ${run.inv} sync`
|
|
35461
|
+
});
|
|
35462
|
+
}
|
|
35463
|
+
await convergingRebase(run, board, commitResult.committed);
|
|
35464
|
+
refuseMovedBoard(board.boardPath);
|
|
35465
|
+
delta = await deltaPhase(board, baseline);
|
|
35466
|
+
holdMalformedCommitted(run, board, delta);
|
|
35467
|
+
continue;
|
|
35468
|
+
}
|
|
35469
|
+
const details = raced ? { ...classified.details, attempts: attempt } : classified.details;
|
|
35470
|
+
const exhausted = raced ? new CliError(
|
|
35471
|
+
classified.code,
|
|
35472
|
+
`another writer pushed to the board each time sync tried (${attempt} attempts, each re-fetched and rebased${gate ? " and re-gated" : ""}) \u2014 re-run sync to try again`,
|
|
35473
|
+
{ details }
|
|
35474
|
+
) : classified;
|
|
35475
|
+
const warning = pushFailureMessage(exhausted);
|
|
35476
|
+
const partial2 = buildPushFailurePartial(
|
|
35477
|
+
board.outcome,
|
|
35478
|
+
warning,
|
|
35479
|
+
commitResult.docs,
|
|
35480
|
+
delta.originDelta,
|
|
35481
|
+
run.limit,
|
|
35482
|
+
delta.reanchorNote,
|
|
35483
|
+
details
|
|
35484
|
+
);
|
|
35485
|
+
run.stdout(render(withSyncEnvelope(partial2, syncEnvelope("git", { received: delta.originDelta.length })), run.mode));
|
|
35486
|
+
await writeAwarenessCache(board.key, board.boardPath, delta.changes, delta.reanchorNote);
|
|
35487
|
+
throw asHandled(new CliError(exhausted.code, warning, { details }));
|
|
35488
|
+
}
|
|
35489
|
+
}
|
|
35490
|
+
}
|
|
35491
|
+
function holdMalformedCommitted(run, board, delta) {
|
|
35492
|
+
const originRef = resolveOriginRef(board.boardPath);
|
|
35493
|
+
if (originRef === null) return;
|
|
35494
|
+
const held2 = malformedCommittedDocuments(board.boardPath, originRef, "HEAD");
|
|
35495
|
+
if (held2.length > 0) reportHeld(run, board, delta, held2, "committed locally");
|
|
35496
|
+
}
|
|
35497
|
+
async function failGate(run, board, commitResult, delta, reason, details) {
|
|
35498
|
+
const warning = `committed to the board locally \u2014 your work is saved. The sync gate ${reason} on the rebased board, so nothing was pushed; fix what it reports, then re-run sync`;
|
|
35499
|
+
const help2 = `run the gate yourself from ${board.boardPath} (git config --get ${SYNC_GATE_CONFIG}), fix what it reports, then ${run.inv} sync`;
|
|
35500
|
+
const partial2 = buildPushFailurePartial(
|
|
35501
|
+
board.outcome,
|
|
35502
|
+
warning,
|
|
35503
|
+
commitResult.docs,
|
|
35504
|
+
delta.originDelta,
|
|
35505
|
+
run.limit,
|
|
35506
|
+
delta.reanchorNote,
|
|
35507
|
+
{ gate: details }
|
|
35508
|
+
);
|
|
35509
|
+
partial2.code = "GATE_FAILED";
|
|
35510
|
+
partial2.help = help2;
|
|
35511
|
+
const envelope2 = syncEnvelope("git", { received: delta.originDelta.length, next: [`${run.inv} sync`] });
|
|
35512
|
+
run.stdout(render(withSyncEnvelope(partial2, envelope2), run.mode));
|
|
35513
|
+
await writeAwarenessCache(board.key, board.boardPath, delta.changes, delta.reanchorNote);
|
|
35514
|
+
throw new CliError("GATE_FAILED", `the sync gate ${reason}; nothing was pushed`, {
|
|
35515
|
+
help: help2,
|
|
35516
|
+
details: { gate: details },
|
|
35517
|
+
handled: true
|
|
35518
|
+
});
|
|
35176
35519
|
}
|
|
35177
35520
|
async function receiptPhase(run, board, commitResult, delta, pushed, establishAlreadyNote) {
|
|
35178
35521
|
await writeAwarenessCache(board.key, board.boardPath, delta.changes, delta.reanchorNote);
|
|
@@ -35187,6 +35530,7 @@ async function receiptPhase(run, board, commitResult, delta, pushed, establishAl
|
|
|
35187
35530
|
reanchorNote: delta.reanchorNote,
|
|
35188
35531
|
hookHint
|
|
35189
35532
|
});
|
|
35533
|
+
if (pushed.gate) receipt3.gate = `passed: ${String(pushed.gate.command)} (attempt ${String(pushed.gate.attempt)})`;
|
|
35190
35534
|
run.stdout(render(withSyncEnvelope(receipt3, syncEnvelope("git", { sent: pushed.documents, received: delta.originDelta.length })), run.mode));
|
|
35191
35535
|
}
|
|
35192
35536
|
async function heldRun(run, board, baseline, held2) {
|
|
@@ -35279,15 +35623,11 @@ async function syncCommand(argv2, deps = {}) {
|
|
|
35279
35623
|
await pullPhase(run, board, commitResult);
|
|
35280
35624
|
if (!run.pullOnly) refuseMovedBoard(board.boardPath);
|
|
35281
35625
|
const delta = await deltaPhase(board, baseline);
|
|
35282
|
-
|
|
35283
|
-
|
|
35284
|
-
|
|
35285
|
-
if (held2.length > 0) reportHeld(run, board, delta, held2, "committed locally");
|
|
35286
|
-
}
|
|
35287
|
-
const pushed = await pushPhase(run, board, commitResult, delta);
|
|
35288
|
-
await receiptPhase(run, board, commitResult, delta, pushed, establishAlreadyNote);
|
|
35626
|
+
if (!run.pullOnly) holdMalformedCommitted(run, board, delta);
|
|
35627
|
+
const pushed = await pushPhase(run, board, commitResult, delta, baseline);
|
|
35628
|
+
await receiptPhase(run, board, commitResult, pushed.delta, pushed, establishAlreadyNote);
|
|
35289
35629
|
}
|
|
35290
|
-
var SYNC_USAGE, DEFAULT_LIMIT, SYNC_LOCAL_ONLY_MESSAGE, SYNC_REMOTE_STATE_UNKNOWN_MESSAGE, SYNC_IN_TREE_BOARD_LINE, SYNC_IN_TREE_NO_BASIS, SYNC_IN_TREE_CURRENT;
|
|
35630
|
+
var SYNC_USAGE, DEFAULT_LIMIT, SYNC_LOCAL_ONLY_MESSAGE, SYNC_REMOTE_STATE_UNKNOWN_MESSAGE, SYNC_IN_TREE_BOARD_LINE, SYNC_IN_TREE_NO_BASIS, SYNC_IN_TREE_CURRENT, SYNC_PUSH_ATTEMPTS;
|
|
35291
35631
|
var init_orchestrate = __esm({
|
|
35292
35632
|
"../cli/src/commands/sync/orchestrate.ts"() {
|
|
35293
35633
|
"use strict";
|
|
@@ -35302,6 +35642,7 @@ var init_orchestrate = __esm({
|
|
|
35302
35642
|
init_establish();
|
|
35303
35643
|
init_converge();
|
|
35304
35644
|
init_show_incoming();
|
|
35645
|
+
init_gate();
|
|
35305
35646
|
init_sync_outcomes();
|
|
35306
35647
|
init_errors4();
|
|
35307
35648
|
init_args();
|
|
@@ -35349,6 +35690,11 @@ board changes.
|
|
|
35349
35690
|
The same verb also works when an automation or chat agent clones the shared \`board\` branch as
|
|
35350
35691
|
the repository root: a tracked OKF root index plus the exact attached \`board\` branch identifies
|
|
35351
35692
|
that standalone checkout, so sync operates there directly and never creates a nested bundle.
|
|
35693
|
+
A repository can carry more than one board: a NAMED board lives on its own \`board-<name>\` branch
|
|
35694
|
+
(lowercase letters, digits and hyphens) whose root commits \`.superbee-board.json\` containing
|
|
35695
|
+
\`{"schema": 1, "branch": "board-<name>"}\`. A standalone clone of that branch, tracking
|
|
35696
|
+
\`origin/board-<name>\`, syncs exactly as above against \`origin/board-<name>\` and never touches
|
|
35697
|
+
\`origin/board\`; the conventional \`.superbee\` worktree always uses \`board\`.
|
|
35352
35698
|
\`--establish\` on an already-established project is a safe no-op that notes \`already established\`
|
|
35353
35699
|
and proceeds as an ordinary sync.
|
|
35354
35700
|
|
|
@@ -35392,7 +35738,25 @@ any error envelope) on stderr. A doc absent upstream renders as an expected stat
|
|
|
35392
35738
|
|
|
35393
35739
|
If the push fails after a local commit already landed (offline, revoked/expired credentials, or a
|
|
35394
35740
|
locked repository), the receipt still reports what committed/pulled successfully \u2014 your work is
|
|
35395
|
-
saved locally either way, and re-running sync retries the push.
|
|
35741
|
+
saved locally either way, and re-running sync retries the push. A push that loses a race to
|
|
35742
|
+
another writer (non-fast-forward, including the remote's "incorrect old value" refusal) is not a
|
|
35743
|
+
permission problem: sync re-fetches, rebases with the same converging mechanic, re-runs the gate,
|
|
35744
|
+
and pushes again, up to 5 attempts in all, then exits 1 with \`details.reason: non-fast-forward\`.
|
|
35745
|
+
|
|
35746
|
+
A clone can declare a SYNC GATE: a command sync runs on the converged tree after rebasing onto
|
|
35747
|
+
the remote board and before every push that sends commits (\`git config superbee.syncGate
|
|
35748
|
+
'<command>'\`, any Git config scope; an empty value declares none; \`superbee.syncGateTimeoutSeconds\`
|
|
35749
|
+
overrides the 600s timeout). It runs through the shell from the board root with
|
|
35750
|
+
SUPERBEE_BOARD_BRANCH, SUPERBEE_BOARD_UPSTREAM_REF, SUPERBEE_BOARD_UPSTREAM_SHA (the remote tip
|
|
35751
|
+
rebased onto), SUPERBEE_BOARD_HEAD_SHA (what would be pushed) and SUPERBEE_SYNC_ATTEMPT set. A
|
|
35752
|
+
non-zero exit, a timeout, or any edit to the board holds the push: the work stays committed
|
|
35753
|
+
locally and sync exits 5 with code GATE_FAILED and the gate's last output lines. Exactly the
|
|
35754
|
+
judged commit is pushed. The command comes from Git configuration, never from board files, so
|
|
35755
|
+
board content cannot choose the command; keep the gate's code outside the board too (a script
|
|
35756
|
+
inside the board runs whatever a teammate last pushed). A gate must leave the board as it found
|
|
35757
|
+
it: an untracked, non-ignored file it writes counts as an edit. The opt-in turn-end sync runs
|
|
35758
|
+
the gate as well, inside the agent host's hook time limit, so keep a gate fast there.
|
|
35759
|
+
\`--pull-only\` never runs it, and neither does \`--establish\`'s first publication of a board.
|
|
35396
35760
|
|
|
35397
35761
|
A board can also ride IN-TREE: \`.superbee/\` or legacy \`.agentstate-lite/\` committed WITH the code on the current
|
|
35398
35762
|
branch, with no dedicated \`board\` branch anywhere. That is a supported, read-side mode \u2014 sync
|
|
@@ -35462,6 +35826,7 @@ Options:
|
|
|
35462
35826
|
SYNC_IN_TREE_BOARD_LINE = syncInTreeBoardLine(BUNDLE_DIR);
|
|
35463
35827
|
SYNC_IN_TREE_NO_BASIS = "no-comparison-basis";
|
|
35464
35828
|
SYNC_IN_TREE_CURRENT = "checkout is current with upstream";
|
|
35829
|
+
SYNC_PUSH_ATTEMPTS = 5;
|
|
35465
35830
|
}
|
|
35466
35831
|
});
|
|
35467
35832
|
|
|
@@ -83884,6 +84249,9 @@ bundle's compatible storage coordinate through each document's declared Kind.
|
|
|
83884
84249
|
|
|
83885
84250
|
// ../cli/src/kind-draft.ts
|
|
83886
84251
|
import { createHash as createHash15 } from "node:crypto";
|
|
84252
|
+
function excludedInferenceField(field) {
|
|
84253
|
+
return EXCLUDED_FIELDS.has(field) || isManagedDocumentMetadataField(field) || field.startsWith("superbee_") && field !== SUPERBEE_PROGRESS_STATUS_FIELD;
|
|
84254
|
+
}
|
|
83887
84255
|
function kindDeclaresAnything(kind2) {
|
|
83888
84256
|
return Object.values(DECLARES).some((declares) => declares(kind2));
|
|
83889
84257
|
}
|
|
@@ -83934,7 +84302,7 @@ function inferKindCandidate(type, instances, stats = collectInstanceStats(instan
|
|
|
83934
84302
|
const optional2 = [];
|
|
83935
84303
|
const values = {};
|
|
83936
84304
|
for (const [key2, present] of Object.entries(stats.keyPresence)) {
|
|
83937
|
-
if (
|
|
84305
|
+
if (excludedInferenceField(key2)) continue;
|
|
83938
84306
|
if (present === count5 && count5 > 0) {
|
|
83939
84307
|
required2.push(key2);
|
|
83940
84308
|
if (count5 >= ENUM_MIN_INSTANCES && everyValueScalarString(instances, key2)) {
|
|
@@ -84010,7 +84378,7 @@ var init_kind_draft = __esm({
|
|
|
84010
84378
|
ENUM_MIN_DISTINCT = 2;
|
|
84011
84379
|
ENUM_MAX_DISTINCT = 6;
|
|
84012
84380
|
SECTION_PROMOTION_FLOOR = 0.6;
|
|
84013
|
-
EXCLUDED_FIELDS = /* @__PURE__ */ new Set([...RESERVED_KIND_FIELD_NAMES,
|
|
84381
|
+
EXCLUDED_FIELDS = /* @__PURE__ */ new Set([...RESERVED_KIND_FIELD_NAMES, "head_version"]);
|
|
84014
84382
|
FIELDS_DECLARES = {
|
|
84015
84383
|
required: (f2) => f2.required.length > 0,
|
|
84016
84384
|
optional: (f2) => f2.optional.length > 0,
|
|
@@ -84147,9 +84515,9 @@ function renderKindHelp(kind2, registry2, inv) {
|
|
|
84147
84515
|
${sectionLines}
|
|
84148
84516
|
` + linksBlock + `${pathLine}
|
|
84149
84517
|
|
|
84150
|
-
Repeat a flag to set an array value (e.g. --tag a --tag b). Validation is STRICT.
|
|
84518
|
+
Repeat a flag to set an array value (e.g. --tag a --tag b). Validation is STRICT; violations reject without creating a document.
|
|
84151
84519
|
` + (req.includes("timestamp") || opt.includes("timestamp") ? `In OKF v0.2, --timestamp requires a real ISO-8601 date and time with an explicit UTC offset (Z or numeric).
|
|
84152
|
-
` : "") + `To ADD a field
|
|
84520
|
+
` : "") + `To ADD a field: ${inv} kind field ${commandToken(kind2.governs)} add <name> [--required] [--values <a,b,c>].
|
|
84153
84521
|
|
|
84154
84522
|
Options:
|
|
84155
84523
|
--stale-after <iso> Expiration instant (OKF v0.2 only); valid date, time, and zone required
|
|
@@ -85937,7 +86305,7 @@ async function prepareRecipeEvolution(bundle, sourceRecipe) {
|
|
|
85937
86305
|
id: doc2.id,
|
|
85938
86306
|
current_version: contentVersion(doc2)
|
|
85939
86307
|
});
|
|
85940
|
-
for (const warning of validateAgainstKind(doc2, kind2)) {
|
|
86308
|
+
for (const warning of projectKindValidationWarnings(okfVersion, kind2, validateAgainstKind(doc2, kind2))) {
|
|
85941
86309
|
evolutionBlocker(
|
|
85942
86310
|
blockers,
|
|
85943
86311
|
"RECIPE_EVOLUTION_INSTANCE_INVALID",
|
|
@@ -86555,9 +86923,9 @@ async function status(argv2, deps = {}) {
|
|
|
86555
86923
|
for (const doc2 of docs) {
|
|
86556
86924
|
const kind2 = registry2.kinds.get(docType2(doc2));
|
|
86557
86925
|
if (!kind2) continue;
|
|
86558
|
-
for (const w2 of validateAgainstKind(doc2, kind2)) {
|
|
86926
|
+
for (const w2 of projectKindValidationWarnings(okfVersion, kind2, validateAgainstKind(doc2, kind2))) {
|
|
86559
86927
|
lintRows.push({ id: doc2.id, field: w2.field ?? "", code: w2.code });
|
|
86560
|
-
if (
|
|
86928
|
+
if (isKindFieldValidationWarning(w2) && !conformanceDebtDocs.has(doc2.id)) {
|
|
86561
86929
|
conformanceDebtDocs.set(doc2.id, docType2(doc2));
|
|
86562
86930
|
}
|
|
86563
86931
|
}
|
|
@@ -86856,7 +87224,7 @@ async function status(argv2, deps = {}) {
|
|
|
86856
87224
|
}
|
|
86857
87225
|
stdout(render({ home: "local", sync: "none (local only)", ...unboundCopyOf(facts), ...out }, resolveMode(values)));
|
|
86858
87226
|
}
|
|
86859
|
-
var STATUS_USAGE, DEFAULT_LIMIT3
|
|
87227
|
+
var STATUS_USAGE, DEFAULT_LIMIT3;
|
|
86860
87228
|
var init_status2 = __esm({
|
|
86861
87229
|
"../cli/src/commands/status.ts"() {
|
|
86862
87230
|
"use strict";
|
|
@@ -87037,7 +87405,6 @@ Options:
|
|
|
87037
87405
|
-h, --help Show this help
|
|
87038
87406
|
`;
|
|
87039
87407
|
DEFAULT_LIMIT3 = 20;
|
|
87040
|
-
FRONTMATTER_VIOLATION_CODES = /* @__PURE__ */ new Set(["KIND_FIELD_MISSING", "KIND_FIELD_VALUE", "KIND_FIELD_ARITY"]);
|
|
87041
87408
|
}
|
|
87042
87409
|
});
|
|
87043
87410
|
|
|
@@ -125254,9 +125621,9 @@ function manifestContent(assets) {
|
|
|
125254
125621
|
return `${JSON.stringify(manifest, null, 2)}
|
|
125255
125622
|
`;
|
|
125256
125623
|
}
|
|
125257
|
-
function unmanagedExtras(dir,
|
|
125624
|
+
function unmanagedExtras(dir, managed) {
|
|
125258
125625
|
return listFilesRelative(dir).filter(
|
|
125259
|
-
(f2) => f2 !== SKILL_MANIFEST_FILENAME && !
|
|
125626
|
+
(f2) => f2 !== SKILL_MANIFEST_FILENAME && !managed.has(f2) && !isManagedDebris(f2, managed)
|
|
125260
125627
|
);
|
|
125261
125628
|
}
|
|
125262
125629
|
function isDirectory(p2) {
|
|
@@ -125304,10 +125671,10 @@ function preflightInstallIntoDir(dir, assets) {
|
|
|
125304
125671
|
reason: `installed skill uses newer compatibility contract ${manifest.compatibility_contract}; running CLI contract ${assets.compatibilityContract} will not downgrade it`
|
|
125305
125672
|
};
|
|
125306
125673
|
}
|
|
125307
|
-
const
|
|
125308
|
-
const obstructed = directoryObstructions(dir,
|
|
125674
|
+
const managed = /* @__PURE__ */ new Set([...manifest.files, ...assets.files, SKILL_MANIFEST_FILENAME]);
|
|
125675
|
+
const obstructed = directoryObstructions(dir, managed);
|
|
125309
125676
|
if (obstructed.length > 0) return directoryObstructionRefusal(obstructed);
|
|
125310
|
-
const extras = unmanagedExtras(dir,
|
|
125677
|
+
const extras = unmanagedExtras(dir, managed);
|
|
125311
125678
|
if (extras.length > 0) {
|
|
125312
125679
|
return {
|
|
125313
125680
|
ok: false,
|
|
@@ -125396,10 +125763,10 @@ function preflightUninstallFromDir(dir) {
|
|
|
125396
125763
|
if (manifest === null) {
|
|
125397
125764
|
return { ok: false, reason: `${SKILL_MANIFEST_FILENAME} does not prove ownership \u2014 refusing to delete anything from a folder in an unknown state` };
|
|
125398
125765
|
}
|
|
125399
|
-
const
|
|
125400
|
-
const obstructed = directoryObstructions(dir,
|
|
125766
|
+
const managed = /* @__PURE__ */ new Set([...manifest.files, SKILL_MANIFEST_FILENAME]);
|
|
125767
|
+
const obstructed = directoryObstructions(dir, managed);
|
|
125401
125768
|
if (obstructed.length > 0) return directoryObstructionRefusal(obstructed);
|
|
125402
|
-
const extras = unmanagedExtras(dir,
|
|
125769
|
+
const extras = unmanagedExtras(dir, managed);
|
|
125403
125770
|
if (extras.length > 0) {
|
|
125404
125771
|
return {
|
|
125405
125772
|
ok: false,
|
|
@@ -126863,12 +127230,12 @@ function runPassiveUpdateOrientation(deps = {}) {
|
|
|
126863
127230
|
releaseActiveUpdateLease(home2, claimed.lease.token);
|
|
126864
127231
|
return void 0;
|
|
126865
127232
|
}
|
|
126866
|
-
const
|
|
127233
|
+
const spawn5 = deps.spawn ?? ((command, argv2, options) => currentHost().spawnChild(command, argv2, options));
|
|
126867
127234
|
if (!activeLeaseAuthority(home2, claimed.lease.token, now())) {
|
|
126868
127235
|
return void 0;
|
|
126869
127236
|
}
|
|
126870
127237
|
try {
|
|
126871
|
-
const child =
|
|
127238
|
+
const child = spawn5(
|
|
126872
127239
|
process.execPath,
|
|
126873
127240
|
[entry, "__update-refresh-v1", claimed.lease.token],
|
|
126874
127241
|
{ detached: true, stdio: "ignore" }
|