Summary
BookmarkSweep.ts, InstallBookmarkSweep.ts and com.lifeos.bookmarksweep.plist.template ship in the public install payload, but the sweep's only two dependencies live in skills/_X/, an underscore-prefixed private skill that is not in the payload. On a public install the launchd job therefore fails on its very first step, forever, every 30 minutes.
Evidence
Shipped in the payload:
LifeOS/install/LIFEOS/TOOLS/BookmarkSweep.ts
LifeOS/install/LIFEOS/TOOLS/InstallBookmarkSweep.ts
LifeOS/install/LIFEOS/TOOLS/com.lifeos.bookmarksweep.plist.template
Not shipped — LifeOS/install/skills/ contains 53 skills and no underscore-prefixed ones; skills/_X returns 404.
The hard dependency, BookmarkSweep.ts lines 43-45:
const X_DIR = join(HOME, ".claude", "skills", "_X");
const BOOKMARKS_TOOL = join(X_DIR, "Tools", "bookmarks.ts");
const BOOKMARK_ISSUE_TOOL = join(X_DIR, "Tools", "bookmark-issue.ts");
fetchBookmarks() spawns bun <BOOKMARKS_TOOL> fetch 50 --json. With the file absent, bun exits 1, so the sweep returns { error: "fetch exited 1" } and exits 0 having done nothing.
Observed on one public install: 458 consecutive runs, 0 successes, spanning two system versions and starting the day the job was first bootstrapped. Every log line is byte-identical apart from the timestamp:
{"ts":"...","fetched":0,"new":0,"created":0,"skipped":0,"auditExit":null,"issueUrls":[],"error":"fetch exited 1"}
Why this is a defect and not just an unused feature
DOCUMENTATION/Work/WorkSystem.md already documents the exclusion policy for exactly this class of component:
Private component — NOT in the public release payload. hooks/ULWorkSync.hook.ts and the _ULWORK skill are principal-specific (they target the private UL work repo) and are rsync-excluded from public releases, the same as any underscore-prefixed private skill.
BookmarkSweep.ts is in the same class. Its classifier prompt triages bookmarks into "UL work ideas" and its VALID_PROPERTIES set is newsletter, website, youtube, podcast, community, consulting, open-source, internal — a principal-specific taxonomy. But unlike ULWorkSync.hook.ts, the tool and its installer were not excluded.
The failure is also silent by design: the sweep logs to MEMORY/OBSERVABILITY/bookmarksweep.jsonl and always exits 0, so launchd reports a healthy job and nothing surfaces. A public installer who bootstraps it gets a permanently dead 30-minute timer that looks alive.
Proposed fix
Preferred — exclude the three files from the payload, consistent with _ULWORK and ULWorkSync.hook.ts. Nothing in install.sh references InstallBookmarkSweep, so no install path breaks.
If the tool should keep shipping, make the installer fail closed instead of arming a job that cannot work. In InstallBookmarkSweep.ts, before materializing the plist:
const BOOKMARKS_TOOL = join(HOME, ".claude", "skills", "_X", "Tools", "bookmarks.ts");
async function install(): Promise<void> {
if (!existsSync(BOOKMARKS_TOOL)) {
console.error(
`[InstallBookmarkSweep] refusing to install: ${BOOKMARKS_TOOL} not found.\n` +
`BookmarkSweep depends on the private _X skill, which is not part of the public payload.`,
);
process.exit(1);
}
// ... existing install path
}
That turns a permanently silent runtime failure into one loud message at the only moment a human is watching.
A third option, if neither fits: have fetchBookmarks() distinguish "dependency missing" from "fetch failed", so the log line says which of the two happened rather than reporting a generic exit code 458 times in a row.
Summary
BookmarkSweep.ts,InstallBookmarkSweep.tsandcom.lifeos.bookmarksweep.plist.templateship in the public install payload, but the sweep's only two dependencies live inskills/_X/, an underscore-prefixed private skill that is not in the payload. On a public install the launchd job therefore fails on its very first step, forever, every 30 minutes.Evidence
Shipped in the payload:
LifeOS/install/LIFEOS/TOOLS/BookmarkSweep.tsLifeOS/install/LIFEOS/TOOLS/InstallBookmarkSweep.tsLifeOS/install/LIFEOS/TOOLS/com.lifeos.bookmarksweep.plist.templateNot shipped —
LifeOS/install/skills/contains 53 skills and no underscore-prefixed ones;skills/_Xreturns 404.The hard dependency,
BookmarkSweep.tslines 43-45:fetchBookmarks()spawnsbun <BOOKMARKS_TOOL> fetch 50 --json. With the file absent, bun exits 1, so the sweep returns{ error: "fetch exited 1" }and exits 0 having done nothing.Observed on one public install: 458 consecutive runs, 0 successes, spanning two system versions and starting the day the job was first bootstrapped. Every log line is byte-identical apart from the timestamp:
{"ts":"...","fetched":0,"new":0,"created":0,"skipped":0,"auditExit":null,"issueUrls":[],"error":"fetch exited 1"}Why this is a defect and not just an unused feature
DOCUMENTATION/Work/WorkSystem.mdalready documents the exclusion policy for exactly this class of component:BookmarkSweep.tsis in the same class. Its classifier prompt triages bookmarks into "UL work ideas" and itsVALID_PROPERTIESset isnewsletter, website, youtube, podcast, community, consulting, open-source, internal— a principal-specific taxonomy. But unlikeULWorkSync.hook.ts, the tool and its installer were not excluded.The failure is also silent by design: the sweep logs to
MEMORY/OBSERVABILITY/bookmarksweep.jsonland always exits 0, so launchd reports a healthy job and nothing surfaces. A public installer who bootstraps it gets a permanently dead 30-minute timer that looks alive.Proposed fix
Preferred — exclude the three files from the payload, consistent with
_ULWORKandULWorkSync.hook.ts. Nothing ininstall.shreferencesInstallBookmarkSweep, so no install path breaks.If the tool should keep shipping, make the installer fail closed instead of arming a job that cannot work. In
InstallBookmarkSweep.ts, before materializing the plist:That turns a permanently silent runtime failure into one loud message at the only moment a human is watching.
A third option, if neither fits: have
fetchBookmarks()distinguish "dependency missing" from "fetch failed", so the log line says which of the two happened rather than reporting a generic exit code 458 times in a row.