Compose immutable dependency sources and preserve branch transitions

Why:
Replace repeated submodule source pointers with consumer-owned language versions and fixed application sources, keeping independent repository edits and existing branches intact.

Evidence:
SELF task receipts contain startup/sync evidence, real Go and npm consumption, Native overlay/unlink, safe transition/ref preservation, credential boundary and historical build regressions. Exact committed full composition and RDM checks follow this checkpoint; formal acceptance and GATE CI remain pending.

Impact:
Add isolated build/test composition, source provenance and safe owner refresh, adapt governance and service build callers, remove replaced index links while retaining original Git metadata, and adopt reusable dependency standards.

Refs:
Documents approved dependency-workspace r1/R1-R15 and U1-U7 implementation plan; feature/dependency-workspace task card.
This commit is contained in:
2026-10-13 17:18:07 +00:00
parent b450e51c40
commit 93ac63fb3d
55 changed files with 12113 additions and 852 deletions
+48 -3
View File
@@ -65,7 +65,43 @@ class Workspace:
self.state.data["components"][component["id"]] = record
return record
def prepare_one(self, component):
def refresh_versioned(self, component, path, record, commit, source):
old_commit = record["baseline"]
old_source = record["source"]
reference = "refs/workspace/fixed/" + old_commit
try:
self.git.call(path, "fetch", "--no-tags", "origin", commit)
# Fetch can take time. Recheck ownership and files before changing
# this registered checkout, without forcing any owner's changes.
actual = self.git.safe(path, component["repo"])
if actual["branch"] or actual["head"] != old_commit:
raise WorkspaceError("fixed checkout changed during fetch; owner must inspect it")
saved = self.git.text(path, "rev-parse", "--verify", "--quiet", reference, check=False)
if saved and saved != old_commit:
raise WorkspaceError("preserved fixed reference has another value; owner must inspect it")
if not saved:
self.git.call(path, "update-ref", reference, old_commit, "0" * 40)
self.git.call(path, "checkout", "--no-overwrite-ignore", "--detach", commit)
actual = self.git.safe(path, component["repo"])
if actual["head"] != commit or actual["branch"]:
raise WorkspaceError("refreshed checkout differs from the declared fixed source")
history = list(record.get("previous_sources", []))
previous = {"commit": old_commit, "source": old_source, "ref": reference}
if previous not in history:
history.append(previous)
record.update({"baseline": commit, "source": source, "branch": None,
"mode": "versioned", "previous_sources": history})
record.pop("error", None)
self.state.save()
return record
except Exception as error:
# In particular, a failed fetch retains the old baseline and mode,
# so retrying the explicit refresh cannot mistake it for a partial clone.
record["error"] = str(error)
self.state.save()
raise
def prepare_one(self, component, refresh_fixed=False):
name = component["id"]
path = safe_path(self.root, component["path"])
commit, source = self.baseline(component)
@@ -75,7 +111,12 @@ class Workspace:
if path.exists():
editable = record and record.get("mode") in ("editable", "linked")
partial = record and record.get("mode") == "preparing"
actual = self.git.safe(path, component["repo"], clean=not (editable or partial))
actual = self.git.safe(path, component["repo"], clean=refresh_fixed or not (editable or partial))
if refresh_fixed:
if not record or record.get("mode") != "versioned" or actual["branch"] or actual["head"] != record["baseline"]:
raise WorkspaceError(f"fixed refresh requires a registered detached versioned checkout at its baseline: {name}")
if actual["head"] != commit:
return self.refresh_versioned(component, path, record, commit, source)
if editable:
if record["baseline"] != commit:
raise WorkspaceError(f"version changed while {name} is editable; owner must synchronize explicitly")
@@ -91,6 +132,7 @@ class Workspace:
if record is None:
record = self.record(component, commit, source)
record.update({"mode": "versioned", "baseline": commit, "source": source})
record.pop("error", None)
self.state.save()
return record
elif record and record.get("mode") != "preparing":
@@ -197,6 +239,7 @@ def main(argv=None):
parser.add_argument("--name")
parser.add_argument("--branch")
parser.add_argument("--apply", action="store_true")
parser.add_argument("--refresh-fixed", action="store_true", help="explicitly refresh registered clean detached fixed sources")
args = parser.parse_args(argv)
try:
if args.branch:
@@ -205,6 +248,8 @@ def main(argv=None):
raise WorkspaceError("--name is required for component editing/unlinking")
if args.apply and args.command != "transition":
raise WorkspaceError("--apply is only valid for transition")
if args.refresh_fixed and args.command != "prepare":
raise WorkspaceError("--refresh-fixed is only valid for prepare")
workspace = Workspace(args.root)
components = workspace.manifest.select(args.name, fixed=args.command in ("prepare", "verify"))
if args.command == "status":
@@ -223,7 +268,7 @@ def main(argv=None):
workspace.transition.apply_all(components, args.branch)
for component in components:
if args.command == "prepare":
workspace.prepare_one(component)
workspace.prepare_one(component, refresh_fixed=args.refresh_fixed)
elif args.command == "edit":
workspace.edit(component, args.branch)
elif args.command == "unlink":