@brainervirus/workit-core 8.6.0 → 8.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-core",
3
- "version": "8.6.0",
3
+ "version": "8.6.1",
4
4
  "private": false,
5
5
  "description": "Workit shared core — task, policy, evidence, review, decision, worker, and writer state for agentic coding workflows",
6
6
  "keywords": [
package/src/git/ops.ts CHANGED
@@ -811,6 +811,32 @@ export type PushOutcome = {
811
811
  };
812
812
 
813
813
  const SHA = /^[0-9a-f]{40,64}$/u;
814
+ const SHA_PREFIX = /^[0-9a-f]{7,39}$/u;
815
+
816
+ /** Resolve an abbreviated `--expect` against the known remote tips; refuse unknown or ambiguous. */
817
+ export function resolveExpectPrefix(
818
+ prefix: string,
819
+ tips: ReadonlyArray<string | null | undefined>,
820
+ label: string,
821
+ ): { ok: true; sha: string } | { ok: false; code: "invalid_input"; error: string } {
822
+ const known = [...new Set(tips.filter((tip): tip is string => typeof tip === "string"))];
823
+ if (!SHA_PREFIX.test(prefix))
824
+ return {
825
+ ok: false,
826
+ code: "invalid_input",
827
+ error: "--expect must be a commit sha (full, or an abbreviation of at least 7 hex digits)",
828
+ };
829
+ const matches = known.filter((tip) => tip.startsWith(prefix));
830
+ if (matches.length === 1) return { ok: true, sha: matches[0] };
831
+ return {
832
+ ok: false,
833
+ code: "invalid_input",
834
+ error:
835
+ matches.length > 1
836
+ ? `--expect ${prefix} is ambiguous for ${label}; choose one of: ${matches.join(", ")}`
837
+ : `--expect ${prefix} matches no known tip of ${label}${known.length ? `; known: ${known.join(", ")}` : ""}; pass the full sha`,
838
+ };
839
+ }
814
840
 
815
841
  export function executePush(
816
842
  cwd: string,
@@ -835,10 +861,18 @@ export function executePush(
835
861
  const args = ["push", "--porcelain", "--no-follow-tags", "--recurse-submodules=no"];
836
862
  if (input.forceWithLease) {
837
863
  const explicit = input.expect !== undefined && input.expect !== null;
838
- if (explicit && !SHA.test(input.expect as string))
839
- return failure("invalid_input", "--expect must be a full commit sha");
864
+ let expectSha = input.expect ?? null;
865
+ if (explicit && !SHA.test(expectSha as string)) {
866
+ const resolved = resolveExpectPrefix(
867
+ expectSha as string,
868
+ [before.sha, recordedRemoteTip(cwd, plan)],
869
+ `${plan.remote}/${plan.branch}`,
870
+ );
871
+ if (!resolved.ok) return failure(resolved.code, resolved.error);
872
+ expectSha = resolved.sha;
873
+ }
840
874
  const integrate = `git fetch ${plan.remote} ${plan.branch} && git rebase ${plan.remote}/${plan.branch} # or merge it; then: workit git push`;
841
- const recorded = explicit ? (input.expect as string) : recordedRemoteTip(cwd, plan);
875
+ const recorded = explicit ? (expectSha as string) : recordedRemoteTip(cwd, plan);
842
876
  // A new branch needs no lease; otherwise workit must have recorded the tip.
843
877
  if (recorded === undefined && before.sha !== null)
844
878
  return failure(
package/src/ledger.ts CHANGED
@@ -1608,7 +1608,11 @@ export function clearStanding(
1608
1608
  * Is `branch` checked out, with uncommitted changes, in another worktree of
1609
1609
  * this repository? (`codeKey` sees only the cwd's own worktree.)
1610
1610
  */
1611
- function dirtyElsewhere(cwd: string, branch: string): boolean {
1611
+ function dirtyElsewhere(cwd: string, branch: string, head: string): boolean {
1612
+ // A checkout of exactly the branch head (a verifier's detached worktree)
1613
+ // judges what it holds: clean judges that head, whatever state another
1614
+ // checkout of the branch is in; dirty (or unreadable) does not.
1615
+ if (headSha(cwd) === head) return worktreeTree(cwd)?.dirty !== false;
1612
1616
  const list = git(cwd, ["worktree", "list", "--porcelain"]);
1613
1617
  let worktree: string | null = null;
1614
1618
  for (const line of (list ?? "").split("\n")) {
@@ -1668,7 +1672,7 @@ export function recordVerdict(
1668
1672
  if (!ledger.ok) return ledger;
1669
1673
  const key = codeKey(context.cwd, { branch, base: context.base });
1670
1674
  if (!key.head) return err("not_found", `branch "${branch}" has no commit to judge`);
1671
- if (key.dirty === true || (key.dirty === null && dirtyElsewhere(context.cwd, branch)))
1675
+ if (key.dirty === true || (key.dirty === null && dirtyElsewhere(context.cwd, branch, key.head)))
1672
1676
  return err(
1673
1677
  "blocked",
1674
1678
  `dirty_worktree: ${branch} has uncommitted changes, so its head is not what was judged`,