@awebai/oats 0.25.1 → 0.25.3

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.
@@ -571,6 +571,35 @@ export function driftOf(instanceJson, discovery, { lock } = {}) {
571
571
  return rows;
572
572
  }
573
573
 
574
+ /**
575
+ * soulDriftOf(instanceJson, discovery)
576
+ * → { name, repoKey, commit, current: { commit } | null, status: "current"|"moved"|"missing", reason? } | null
577
+ *
578
+ * Decision 17 for the SOUL SOURCE: a workspace spawn records `instance.json.workspace.soul =
579
+ * { repoKey, commit, team }` (the member and commit the soul was fetched from). The member row
580
+ * for `repoKey` in `discovery.members` is the current state: a different commit → "moved", the
581
+ * soul gone from the member → "missing" (reason "soul-absent"), no usable row → "missing"
582
+ * (reason "unconfirmed" / the row's reason). A standalone view's own member row is unconfirmed
583
+ * by construction (reason "cannot-read") yet carries the repo's current commit — it is compared,
584
+ * not reported missing. Returns null for a home without a recorded workspace soul (classic).
585
+ */
586
+ export function soulDriftOf(instanceJson, discovery) {
587
+ const soul = plainObject(instanceJson?.workspace) && plainObject(instanceJson.workspace.soul) ? instanceJson.workspace.soul : null;
588
+ if (!soul || typeof soul.repoKey !== "string") return null;
589
+ const name = typeof instanceJson.agent === "string" ? instanceJson.agent : (typeof instanceJson.soul === "string" ? instanceJson.soul : null);
590
+ const commit = typeof soul.commit === "string" ? soul.commit : null;
591
+ const members = Array.isArray(discovery?.members) ? discovery.members : [];
592
+ const member = members.find((m) => m && m.key === soul.repoKey) || null;
593
+ const standaloneOwn = discovery?.standalone === true && member && member.key === discovery.key && typeof member.commit === "string";
594
+ const base = { name, repoKey: soul.repoKey, commit, team: soul.team ?? null };
595
+ if (!member || (!member.confirmed && !standaloneOwn) || typeof member.commit !== "string") {
596
+ return { ...base, current: null, status: "missing", reason: member ? (member.reason || "unconfirmed") : "unconfirmed" };
597
+ }
598
+ const present = name === null || (Array.isArray(member.souls) && member.souls.some((s) => s && s.name === name));
599
+ if (!present) return { ...base, current: { commit: member.commit }, status: "missing", reason: "soul-absent" };
600
+ return { ...base, current: { commit: member.commit }, status: member.commit === commit ? "current" : "moved" };
601
+ }
602
+
574
603
  /** Read the modules recorded in an instance home (→ {} when absent). */
575
604
  export function recordedModules(home) {
576
605
  const file = join(resolve(home), "instance.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awebai/oats",
3
- "version": "0.25.1",
3
+ "version": "0.25.3",
4
4
  "description": "OATS (Open Agent Team Specification) — durable souls, disposable instances, targetable capability packages, and the runtime-neutral oats CLI/kernel.",
5
5
  "keywords": [
6
6
  "agents",