feat(cli): persistent skill cache to skip prpm install on repeat launches#124
feat(cli): persistent skill cache to skip prpm install on repeat launches#124khaliqgant wants to merge 4 commits into
Conversation
…ches
Every `agentworkforce agent <persona>` previously spawned a fresh
`npx prpm install …` per declared skill, even when the skill set hadn't
changed — `npx` resolution + registry lookups + tarball fetches added
several seconds of latency to every launch.
Introduce a content-addressed cache under
`~/.agentworkforce/workforce/cache/plugins/<fingerprint>/` keyed by a
SHA-256 of `(harness, sorted skill sources, local file contents)`. On a
hit, the install subprocess is skipped entirely:
- claude reuses the cache dir directly as `--plugin-dir <cacheDir>`.
- opencode / codex mirror the cache contents into the relayfile mount
before launch (mount-ignored patterns keep this from syncing back to
the user's repo).
Invalidation is conservative: changing a skill source string (or editing
a local `.md` skill) rotates the fingerprint and produces a fresh cache
entry. To force-refresh past unchanged remote sources, pass
`--refresh-skills`; to bypass the cache entirely, pass `--no-skill-cache`
or set `AGENTWORKFORCE_NO_SKILL_CACHE=1`.
`--install-in-repo` disengages caching — the user explicitly opted into
having installs land in their working tree, so mirroring a cache back
into the repo would silently re-introduce the leakage that flag is
designed to surface.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a persistent, content-addressed skill-install cache and upstream drift probing; exposes cache/probe APIs via persona-kit; adds CLI flags ( ChangesPersistent skill-cache system
Sequence DiagramsequenceDiagram
participant User
participant CLI as runInteractive
participant PersonaKit as persona-kit
participant Installer
participant CacheFS as cache dir (~/.agentworkforce/...)
User->>CLI: invoke agent (flags)
CLI->>PersonaKit: computeSkillCacheFingerprint(harness, skills)
CLI->>PersonaKit: resolveSkillCacheDir(fingerprint)
CLI->>PersonaKit: readSkillCacheMarker(cacheDir)
alt marker valid and not refresh
CLI->>CacheFS: mirrorSkillCacheInto(cacheDir, target)
else marker missing/stale or refresh requested
CLI->>Installer: run installer into cacheDir
Installer->>PersonaKit: writeSkillCacheMarker(cacheDir, marker)
CLI->>CacheFS: mirrorSkillCacheInto(cacheDir, target)
end
alt marker TTL expired & check-upstream enabled
CLI->>PersonaKit: detectSkillUpstreamDrift(marker)
PersonaKit-->>CLI: drift result
CLI->>Installer: possibly reinstall if drifted
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli/src/cli.test.ts`:
- Around line 142-163: The test relies on parseAgentArgs reading
process.env.AGENTWORKFORCE_NO_SKILL_CACHE which can make it flaky; modify the
test (the 'parseAgentArgs: --no-skill-cache and --refresh-skills set flags'
case) to explicitly isolate that env var by saving the original
process.env.AGENTWORKFORCE_NO_SKILL_CACHE, then unset or set it to a known value
before calling parseAgentArgs and restore the original value after the
assertions; ensure this isolation is applied around each call that expects the
default (false) so parseAgentArgs sees a controlled environment.
In `@packages/cli/src/cli.ts`:
- Around line 1378-1390: When the user requested a refresh, the install
currently runs into the existing cache dir which can preserve
lockfiles/artifacts; update the install flow around the block that checks
install.commandString, deferInstallToMount, and skillCacheHit so that if the
refresh-skills flag is set you delete/clean skillCacheDir (when
skillCacheFingerprint && skillCacheDir are present) before calling
runInstall(install.command, installLabel); apply the same change to the later
analogous install block (the second runInstall/writeSkillCacheMarker block) so
both flows clear the cache dir prior to reinstalling.
- Around line 1299-1303: The cache validity check using skillCacheDir,
skillCacheFingerprint, isSkillCacheValid and options.refreshSkills must be
protected by a per-fingerprint inter-process lock to prevent two processes from
simultaneously installing into the same cache; wrap the
read-check-and-possible-install sequence in an exclusive lock keyed by the
fingerprint (e.g., create/acquire a fingerprint-specific lock file like
`${skillCacheFingerprint}.lock` in the cache dir or use an OS/file-locking
library) before calling isSkillCacheValid and before performing the install path
that writes into the cache, ensure the lock is always released (use try/finally)
and fall back to re-checking isSkillCacheValid after acquiring the lock so
redundant installs are avoided.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2bc42043-8182-4578-81dd-7e002bdf8268
📒 Files selected for processing (5)
packages/cli/src/cli.test.tspackages/cli/src/cli.tspackages/persona-kit/src/index.tspackages/persona-kit/src/skill-cache.test.tspackages/persona-kit/src/skill-cache.ts
| if (skillCacheHit && skillCacheDir) { | ||
| mirrorSkillCacheInto(skillCacheDir, handle.mountDir); | ||
| } else if (skillCachingEnabled && skillCacheDir && skillCacheFingerprint) { | ||
| mkdirSync(skillCacheDir, { recursive: true }); | ||
| await runInstallOrThrow(install.command, installLabel, skillCacheDir); | ||
| writeSkillCacheMarker(skillCacheDir, { | ||
| fingerprint: skillCacheFingerprint, | ||
| harness, | ||
| skills: effectiveSelection.skills.map((s) => ({ | ||
| id: s.id, | ||
| source: s.source | ||
| })) | ||
| }); | ||
| mirrorSkillCacheInto(skillCacheDir, handle.mountDir); | ||
| } else { | ||
| await runInstallOrThrow(install.command, installLabel, handle.mountDir); | ||
| } |
There was a problem hiding this comment.
🔴 --refresh-skills does not remove old cache dir before reinstalling, leaving stale artifacts and risking corrupted cache on failure
When --refresh-skills is used, the code sets skillCacheHit = false at cli.ts:1302 to bypass the cache, but never removes or cleans the existing cache directory before running the install into it. The help text at line 142 says "overwriting any existing cache dir" but the install simply runs on top of the existing contents.
This has two consequences:
- Stale artifacts on success: If an upstream skill package changed structure between versions (removed files), those old files persist in the cache dir and get served to the harness via
--plugin-dir(claude) ormirrorSkillCacheInto(mount harnesses atcli.ts:1611). - Corrupted cache after partial failure: If the refresh install partially completes then fails (
runInstallOrThrowthrows atcli.ts:1602), the old cache marker from the previous successful install remains on disk. On the next run without--refresh-skills,isSkillCacheValidatcli.ts:1303matches the old marker's fingerprint and returns a false cache hit, causing the harness to see a mix of old and partially-updated skill artifacts.
Prompt for agents
The --refresh-skills flag needs to clean the existing cache directory before reinstalling to ensure a truly fresh install. There are two code paths that need this fix:
1. The mount-deferred install path (cli.ts around line 1598-1614): Before the else-if branch that runs runInstallOrThrow into skillCacheDir, the old cache dir should be removed (rmSync with recursive+force) so that stale files dont persist and the old marker is cleared.
2. The claude non-mount install path (cli.ts around line 1378-1391): Similarly, when refreshSkills is true, the cache dir should be cleaned before runInstall runs.
A minimal fix would be to add something like: if (options.refreshSkills && skillCacheDir && existsSync(skillCacheDir)) { rmSync(skillCacheDir, { recursive: true, force: true }); } before the install runs in both paths. This ensures (a) stale files are removed on successful refresh, and (b) the old marker is gone if the refresh fails, so subsequent runs correctly see a cache miss instead of a false hit.
Was this helpful? React with 👍 or 👎 to provide feedback.
…che hit The pure cache-hit path is a near-instant cpSync that doesn't print its own spinner — but the surrounding setupSpinner.stop()/start() was redrawing the "Setting up sandbox mount…" line a second time. Only stop/start the setup spinner around branches that actually run an install spinner of their own. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The source-key fingerprint never rotates on an upstream publish when the
persona's `source` string is unchanged (floating refs like `@org/skill`
or `github.com/org/repo#x`). This adds a cheap, opt-in second check so a
new upstream version is actually consumed without a manual refresh.
Marker schema bumped to v2 (v1 stays read-compatible, upgraded in place
with no upstream records). At install time each remote skill's upstream
identity is recorded from the installer lockfiles:
- prpm → resolved `version` from prpm.lock
- skill.sh → GitHub blob SHA of the installed SKILL.md, located via
skills-lock.json `skillPath`
On launch, if the marker's `lastUpstreamCheckAt` is older than the
interval (default 24h), parallel lightweight probes compare recorded vs.
current — prpm registry GET, GitHub Contents API with `If-None-Match`
(304, no body, when unchanged). Any drift downgrades the cache hit to a
miss so the reinstall picks up the new content; the marker then
self-heals. Every probe fails OPEN: a network error, timeout, 4xx/5xx,
or malformed body counts as "no drift" so a flaky registry never blocks
or slows a launch beyond the timeout.
Control surface:
- `--check-upstream` force a probe this launch (ignore the TTL)
- `--no-check-upstream` skip the probe this launch
- `AGENTWORKFORCE_SKILL_CACHE_CHECK_INTERVAL` (24h default; `0`=always,
`never`/`off`=disable; accepts ms/s/m/h/d, bare number = hours)
Tests: 22 new persona-kit unit tests (mocked HTTP — prpm/github
success, 304, 404/5xx fail-open, timeout, mixed sets, v1→v2 migration,
marker round-trip, TTL math) + 1 CLI flag-parse test. Verified
end-to-end against live prpm.dev and api.github.com: install records
the resolved version, in-TTL launches skip probing, a tampered stale
version triggers a live-detected reinstall (1.0.0 → 1.1.3), the marker
self-heals, and --no-check-upstream bypasses.
A decision trajectory for this work is recorded under
.trajectories/completed/2026-05/traj_47ulsb0rwbid.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/persona-kit/src/skill-cache.test.ts (1)
100-124: 💤 Low valueMinor: Test setup could be simplified.
Lines 103-105 attempt to write to a non-existent path, catch the error silently, then create the directory. This works but is confusing. You could simplify by just doing the
mkdirwith{ recursive: true }first.♻️ Suggested simplification
test('computeSkillCacheFingerprint: local skill content changes invalidate', async () => { await withTmpDir(async (dir) => { const skillPath = 'skills/foo.md'; - await writeFile(join(dir, 'skills', 'foo.md').replace('foo.md', ''), '', 'utf8').catch( - () => undefined - ); - // Ensure the dir exists, then write content. - await import('node:fs/promises').then((fs) => - fs.mkdir(join(dir, 'skills'), { recursive: true }) - ); + const { mkdir } = await import('node:fs/promises'); + await mkdir(join(dir, 'skills'), { recursive: true }); await writeFile(join(dir, skillPath), '# original', 'utf8');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/persona-kit/src/skill-cache.test.ts` around lines 100 - 124, In the test computeSkillCacheFingerprint: local skill content changes invalidate, simplify setup by creating the skills directory before writing files: replace the initial writeFile(...).catch(...) with an explicit await mkdir(join(dir, 'skills'), { recursive: true }) (you already call fs.mkdir later) so that the skills directory exists prior to writeFile; adjust/remove the redundant import/second mkdir call and ensure the test still writes join(dir, skillPath) before computing fingerprints in this test function.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/persona-kit/src/skill-cache.test.ts`:
- Around line 100-124: In the test computeSkillCacheFingerprint: local skill
content changes invalidate, simplify setup by creating the skills directory
before writing files: replace the initial writeFile(...).catch(...) with an
explicit await mkdir(join(dir, 'skills'), { recursive: true }) (you already call
fs.mkdir later) so that the skills directory exists prior to writeFile;
adjust/remove the redundant import/second mkdir call and ensure the test still
writes join(dir, skillPath) before computing fingerprints in this test function.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 73177675-b733-4221-8fb5-cb8a5e00890c
📒 Files selected for processing (10)
.trajectories/completed/2026-05/traj_47ulsb0rwbid.json.trajectories/completed/2026-05/traj_47ulsb0rwbid.md.trajectories/index.jsonpackages/cli/src/cli.test.tspackages/cli/src/cli.tspackages/persona-kit/src/index.tspackages/persona-kit/src/skill-cache.test.tspackages/persona-kit/src/skill-cache.tspackages/persona-kit/src/skill-upstream-probe.test.tspackages/persona-kit/src/skill-upstream-probe.ts
✅ Files skipped from review due to trivial changes (1)
- .trajectories/completed/2026-05/traj_47ulsb0rwbid.json
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/cli/src/cli.test.ts
- packages/cli/src/cli.ts
There was a problem hiding this comment.
3 issues found across 10 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".trajectories/completed/2026-05/traj_47ulsb0rwbid.json">
<violation number="1" location=".trajectories/completed/2026-05/traj_47ulsb0rwbid.json:132">
P2: Avoid committing a user-specific absolute filesystem path in `projectId`; use a stable, repo-agnostic identifier instead.</violation>
</file>
<file name="packages/persona-kit/src/skill-upstream-probe.ts">
<violation number="1" location="packages/persona-kit/src/skill-upstream-probe.ts:344">
P2: Missing upstream metadata for remote skills is forced to `drifted: true`, so transient install-time probe failures can repeatedly trigger unnecessary reinstalls and negate cache reuse.</violation>
</file>
<file name="packages/cli/src/cli.ts">
<violation number="1" location="packages/cli/src/cli.ts:1406">
P2: `AGENTWORKFORCE_SKILL_CACHE_CHECK_INTERVAL=never` is currently ignored because `null` from `parseCheckInterval` is coalesced to the default interval. This causes upstream drift checks to run when the user explicitly asked to disable them.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic
Re-trigger cubic
| ], | ||
| "commits": [], | ||
| "filesChanged": [], | ||
| "projectId": "/Users/khaliqgant/Projects/AgentWorkforce/workforce", |
There was a problem hiding this comment.
P2: Avoid committing a user-specific absolute filesystem path in projectId; use a stable, repo-agnostic identifier instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .trajectories/completed/2026-05/traj_47ulsb0rwbid.json, line 132:
<comment>Avoid committing a user-specific absolute filesystem path in `projectId`; use a stable, repo-agnostic identifier instead.</comment>
<file context>
@@ -0,0 +1,144 @@
+ ],
+ "commits": [],
+ "filesChanged": [],
+ "projectId": "/Users/khaliqgant/Projects/AgentWorkforce/workforce",
+ "tags": [],
+ "_trace": {
</file context>
Tip: Review your code locally with the cubic CLI to iterate faster.
| skillId: skill.id, | ||
| source: skill.source, | ||
| kind: 'prpm', | ||
| drifted: true, |
There was a problem hiding this comment.
P2: Missing upstream metadata for remote skills is forced to drifted: true, so transient install-time probe failures can repeatedly trigger unnecessary reinstalls and negate cache reuse.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/persona-kit/src/skill-upstream-probe.ts, line 344:
<comment>Missing upstream metadata for remote skills is forced to `drifted: true`, so transient install-time probe failures can repeatedly trigger unnecessary reinstalls and negate cache reuse.</comment>
<file context>
@@ -0,0 +1,464 @@
+ skillId: skill.id,
+ source: skill.source,
+ kind: 'prpm',
+ drifted: true,
+ note: 'no upstream record (pre-v2 marker) — reinstall to capture identity'
+ };
</file context>
…-interval Four reviewer findings (CodeRabbit / Devin / cubic): 1. `AGENTWORKFORCE_SKILL_CACHE_CHECK_INTERVAL=never` was ignored: `parseCheckInterval` returns `null` for never/off/false, but the `?? DEFAULT` coalesced `null` back to the 24h default, silently re-enabling drift checks the user disabled. New `resolveEnvCheckIntervalMs()` only falls back on `undefined`. 2. `--refresh-skills` now wipes the cache dir before reinstalling, so a refresh is a true rebuild — stale files from a prior skill version no longer linger, and a partial-failure reinstall can't leave the old marker behind to fake a later cache hit. Applied to both the claude pre-mount path and the mount-deferred path. 3. Added a per-fingerprint advisory install lock (`<cacheDir>.lock`, sibling so a refresh-wipe can't delete a live lock). Two concurrent launches of the same persona no longer both miss and install into the same dir. Stale/dead-pid locks are stolen; if the wait budget is exceeded we proceed unlocked (never hang a launch). After acquiring, validity is re-checked so a peer's just-finished install is reused instead of redundantly reinstalled. 4. Test isolation: the parseAgentArgs flag test now saves/clears/ restores AGENTWORKFORCE_NO_SKILL_CACHE so a machine with that env set can't flake the default-false assertions. Tests: +5 (resolveEnvCheckIntervalMs never/default/0; lock acquire- block-release, stale-steal, dead-pid-steal). Full repo 595/595 green. E2E re-verified: a planted stale file is gone after --refresh-skills and no lock file is left behind. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed all review findings in 9ab2ba7:
Full repo: 595/595 tests green (+5 new). |
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/cli/src/cli.ts">
<violation number="1" location="packages/cli/src/cli.ts:724">
P1: The stale-lock check can steal a live lock after 15 minutes because it returns stale on timestamp age before validating PID liveness, which can re-enable concurrent installs into the same cache dir.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic
Re-trigger cubic
| if (Number.isFinite(ts) && Date.now() - ts > SKILL_CACHE_LOCK_STALE_MS) { | ||
| return true; | ||
| } | ||
| const pid = Number(pidRaw); | ||
| if (Number.isInteger(pid) && pid > 0 && !skillCachePidAlive(pid)) { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
P1: The stale-lock check can steal a live lock after 15 minutes because it returns stale on timestamp age before validating PID liveness, which can re-enable concurrent installs into the same cache dir.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/cli.ts, line 724:
<comment>The stale-lock check can steal a live lock after 15 minutes because it returns stale on timestamp age before validating PID liveness, which can re-enable concurrent installs into the same cache dir.</comment>
<file context>
@@ -677,6 +677,122 @@ function sessionMountDir(sessionRoot: string): string {
+ try {
+ const [pidRaw, tsRaw] = readFileSync(lockPath, 'utf8').split('\n');
+ const ts = Number(tsRaw);
+ if (Number.isFinite(ts) && Date.now() - ts > SKILL_CACHE_LOCK_STALE_MS) {
+ return true;
+ }
</file context>
| if (Number.isFinite(ts) && Date.now() - ts > SKILL_CACHE_LOCK_STALE_MS) { | |
| return true; | |
| } | |
| const pid = Number(pidRaw); | |
| if (Number.isInteger(pid) && pid > 0 && !skillCachePidAlive(pid)) { | |
| return true; | |
| } | |
| const pid = Number(pidRaw); | |
| if (Number.isFinite(ts) && Date.now() - ts > SKILL_CACHE_LOCK_STALE_MS) { | |
| if (!Number.isInteger(pid) || pid <= 0 || !skillCachePidAlive(pid)) { | |
| return true; | |
| } | |
| } | |
| if (Number.isInteger(pid) && pid > 0 && !skillCachePidAlive(pid)) { | |
| return true; | |
| } |
Tip: Review your code locally with the cubic CLI to iterate faster.
Summary
Two layers of caching for
agentworkforce agent <persona>skill installs, so repeat launches stop re-runningnpx prpm install/npx skills add.Layer 1 — persistent source-keyed cache. Content-addressed dir under
~/.agentworkforce/workforce/cache/plugins/<fingerprint>/, keyed by(harness, sorted skill sources, local-file SHA). On a hit the install subprocess is skipped entirely — claude reuses it as--plugin-dir; opencode/codex mirror it into the relayfile mount. Source-string or local-file edits rotate the fingerprint automatically.Layer 2 — opt-in upstream drift detection. Because a floating ref (
@org/skill,github.com/org/repo#x) doesn't change the fingerprint when upstream publishes, the marker (now schema v2) records each remote skill's upstream identity at install time and re-probes it on a TTL:versionfromprpm.lockGET registry.prpm.dev/api/v1/packages/<pkg>→latest_version.versionSKILL.md(path fromskills-lock.json)If-None-Match→ 304 when unchanged.mdIf
now - lastUpstreamCheckAt > interval(default 24h), parallel probes run; any drift downgrades the cache hit to a miss and the reinstall self-heals the marker. Every probe fails open — a network error, timeout, 4xx/5xx, or malformed body is treated as "no drift" so a flaky registry never blocks or slows a launch.Control surface
--refresh-skills--no-skill-cache/AGENTWORKFORCE_NO_SKILL_CACHE=1--check-upstream--no-check-upstreamAGENTWORKFORCE_SKILL_CACHE_CHECK_INTERVAL24hdefault;0=always,never/off=disable; acceptsms/s/m/h/d, bare number = hours--install-in-repoTests
# fail 0) — incl. 12 fingerprint/marker tests, 22 new drift/probe tests (mocked HTTP: prpm & github success,304,404/5xxfail-open, timeout, mixed sets, v1→v2 migration, TTL math, marker round-trip).# fail 0) — incl. flag-parse coverage for all four new flags.End-to-end (real services, not mocked)
Verified against live prpm.dev + api.github.com:
1.0.0→ detects1.0.0 → 1.1.3; recorded current → no drift; GitHub stale SHA → drift; GitHub real SHA →304"unchanged".@agent-relay/choosing-swarm-patterns):prpm install→ marker records resolved1.1.3--check-upstream+ current → "Skills up to date — reusing cache"1.0.0) +--check-upstream→ live-detected drift → reinstall → marker self-heals to 1.1.3--no-check-upstream→ probe skipped, plain cache hit--plugin-dir, opencode mount mirror,--refresh-skills,--no-skill-cache, local-file edit invalidation) all confirmed.Decision trail
A
trailtrajectory documenting the design decisions (cache shape, never-auto-invalidate fingerprint, all-harness scope, drift-probe choice, precise blob SHA vs. repo-HEAD, schema-v2 migration) is committed at.trajectories/completed/2026-05/traj_47ulsb0rwbid.{json,md}.🤖 Generated with Claude Code