@gobing-ai/spur 0.4.17 → 0.4.18

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.
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.4.17",
10
+ "version": "0.4.18",
11
11
  "source": "./plugins/sp"
12
12
  }
13
13
  ]
@@ -661,12 +661,12 @@ states:
661
661
  # which this pointer names.
662
662
  input: >-
663
663
  /sp:dev-review --tasks ${vars.wbs} --auto --focus ${vars.reviewFocus} Answer contract (full: super-reviewer Output
664
- Format). Two artifacts: the `## Review` section (dispositions, deriveReviewConfidence) and your captured answer
665
- (certification input for `spur task verdict`). The answer has one `Confidence: HIGH|MEDIUM|LOW` line, EVIDENCE-derived
666
- (deriveVerifyConfidence), never the section's disposition level. End with one `Verdict: PASS|PARTIAL|FAIL` line
667
- preceded by `| Req | Status | Evidence |` (one row per requirement; MET | PARTIAL | UNMET) and `| AC | Status |
668
- Evidence Type | Evidence |` (one row per AC; MET | PARTIAL | UNMET | N/A). Evidence types (`+` compound allowed): test
669
- | command | static-ref | manual-review | llm-judge | n/a. Evidence cells cite `path:line` or a named check. AC rows
664
+ Format). Two artifacts: the `## Review` section (deriveReviewConfidence) and your captured answer
665
+ (`spur task verdict` input). The answer has one `Confidence: HIGH|MEDIUM|LOW` line, EVIDENCE-derived
666
+ (deriveVerifyConfidence), never the section's disposition level. End with one `Verdict: PASS|PARTIAL|FAIL` line after `| Req | Status | Evidence |` (one row per requirement; MET | PARTIAL | UNMET) and `| AC | Status |
667
+ Evidence Type | Evidence |` (one row per AC; MET | PARTIAL | UNMET | N/A). Evidence types (`+` ok): test
668
+ | command | static-ref | manual-review | llm-judge | n/a. Evidence cells cite a repo-root-relative `path:line`
669
+ (never `/tmp`: out-of-repo blocks done) or a named check. AC rows
670
670
  key on the task's identities or a declared `AC-N` alias; a linked scenario covers its task and a task-local criterion
671
671
  needs no feature credit. Open P1-P3 blocks completion.
672
672
  # The captured review answer is the run's certification input (and the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.4.17",
3
+ "version": "0.4.18",
4
4
  "description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
5
5
  "keywords": [
6
6
  "spur",
@@ -265,7 +265,9 @@ this exact shape:
265
265
  One row per declared requirement (`MET | PARTIAL | UNMET`, none omitted or merged) and one row per
266
266
  acceptance criterion (`MET | PARTIAL | UNMET | N/A`), with the evidence-type token alone in its own
267
267
  cell — `test | command | static-ref | manual-review | llm-judge | n/a`, or a `+` compound when more
268
- than one kind applies. Evidence cells cite a repo-relative `path:line` or a named test/command. AC
268
+ than one kind applies. Evidence cells cite a repo-root-relative `path:line` or a named test/command.
269
+ An absolute path outside the repo (`/tmp/<report>.md:12`) is an unresolved anchor that blocks the
270
+ done projection (`L4.anchor-unresolved`); cite the owning source or a corpus artifact instead. AC
269
271
  identities stay the task's own (checklist label, title, bold head, `Scenario:` prefix, bracket tag or
270
272
  declared `AC-N` alias): a linked feature's scenario title covers the task that implements it, and a
271
273
  task-local criterion is legal as-is — it needs no feature credit.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.4.17",
3
+ "version": "0.4.18",
4
4
  "description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics."
5
5
  }
package/spur.js CHANGED
@@ -120938,11 +120938,16 @@ async function persistWorktreeRuns(input) {
120938
120938
  if (fromStat === undefined || !fromStat.isDirectory()) {
120939
120939
  throw new Error(`persist-out: source worktree does not exist or is not a directory: ${input.fromWorkdir}`);
120940
120940
  }
120941
+ const fs = createNodeFileSystem2();
120942
+ const fromReal = fs.realPath?.(fromDir) ?? fromDir;
120943
+ const toReal = fs.realPath?.(toDir) ?? toDir;
120944
+ if (fromReal === toReal) {
120945
+ throw new Error(`persist-out: source and destination are the same tree (${fromDir}) \u2014 run it from the invoking tree, or the evidence is copied onto itself and lost with the worktree`);
120946
+ }
120941
120947
  const fromRunDir = join67(fromDir, ".spur", "run");
120942
120948
  const toRunDir = join67(toDir, ".spur", "run");
120943
120949
  const fromRecordsDir = runStoragePaths(fromDir).recordsDir;
120944
120950
  const toRecordsDir = runStoragePaths(toDir).recordsDir;
120945
- const fs = createNodeFileSystem2();
120946
120951
  const fromEvidence = runStoragePaths(fromDir).evidenceDir;
120947
120952
  const toEvidence = runStoragePaths(toDir).evidenceDir;
120948
120953
  const evidenceCopies = [];
@@ -146882,7 +146887,7 @@ import { createRequire as createRequire3 } from "module";
146882
146887
  var CLI_CONFIG = {
146883
146888
  binaryName: "spur",
146884
146889
  binaryLabel: "spur",
146885
- binaryVersion: "0.4.17",
146890
+ binaryVersion: "0.4.18",
146886
146891
  configDir: ".spur",
146887
146892
  configFile: ".spur/config.yaml",
146888
146893
  databaseFile: ".spur/spur.db"
@@ -156730,7 +156735,7 @@ init_dist5();
156730
156735
  import { homedir as homedir17 } from "os";
156731
156736
  import { basename as basename20, join as join94, relative as relative19, resolve as resolve37 } from "path";
156732
156737
  // ../server/package.json
156733
- var version3 = "0.4.17";
156738
+ var version3 = "0.4.18";
156734
156739
 
156735
156740
  // ../server/src/modules/health/index.ts
156736
156741
  var startedAt = Date.now();
@@ -161784,6 +161789,7 @@ function registerWorkflowCleanCommand(workflow, context) {
161784
161789
  }
161785
161790
 
161786
161791
  // ../../packages/app/src/workflow/commit-guard.ts
161792
+ init_src();
161787
161793
  init_dist5();
161788
161794
  import { mkdirSync as mkdirSync23, readFileSync as readFileSync28, renameSync as renameSync10, writeFileSync as writeFileSync20 } from "fs";
161789
161795
  import { join as join100 } from "path";
@@ -161954,6 +161960,26 @@ function commitGuardStage(run, paths, options = {}) {
161954
161960
  foreign
161955
161961
  };
161956
161962
  }
161963
+ function foreignSessionOwner(cwd) {
161964
+ try {
161965
+ const raw = JSON.parse(readFileSync28(join100(cwd, ".spur", "context", ".session.json"), "utf8"));
161966
+ const sessionId = typeof raw.owner?.sessionId === "string" ? raw.owner.sessionId : "";
161967
+ if (sessionId === "")
161968
+ return null;
161969
+ const current = currentHostSessionId();
161970
+ if (current === "" || current === sessionId)
161971
+ return null;
161972
+ return { host: typeof raw.owner?.host === "string" ? raw.owner.host : "unknown", sessionId };
161973
+ } catch {
161974
+ return null;
161975
+ }
161976
+ }
161977
+ function currentHostSessionId() {
161978
+ const transcript = getEnvVar("PI_SESSION_FILE") ?? "";
161979
+ const stem = transcript.replace(/\.jsonl$/, "").split("/").pop() ?? "";
161980
+ const pi = stem.includes("_") ? stem.split("_").pop() ?? "" : "";
161981
+ return pi !== "" ? pi : (getEnvVar("CLAUDE_CODE_SESSION_ID") ?? "").trim();
161982
+ }
161957
161983
  function commitGuardCheck(run, options = {}) {
161958
161984
  try {
161959
161985
  const context = runContext(run, options);
@@ -161961,7 +161987,11 @@ function commitGuardCheck(run, options = {}) {
161961
161987
  return missingStart(run);
161962
161988
  const { base, git, written } = context;
161963
161989
  const foreign = [...changedSinceBase(base, git)].filter((path) => !written.has(path)).sort();
161964
- return report(0, [JSON.stringify({ foreign })]);
161990
+ const owner = foreignSessionOwner(options.cwd ?? process.cwd());
161991
+ const stderr = owner ? [
161992
+ `commit-guard: another session owns this tree \u2014 .spur/context/.session.json records ${owner.host} ${owner.sessionId}; use a separate worktree (one writer per tree)`
161993
+ ] : [];
161994
+ return { ...report(0, [JSON.stringify({ foreign, ...owner ? { foreignSession: owner } : {} })], stderr) };
161965
161995
  } catch (err) {
161966
161996
  return report(1, [], [probeDiagnostic(err)]);
161967
161997
  }