Upstream provenance & drift
Quartermaster answers: where did this skill/agent/MCP/rule come from, what version was it based on, is there a newer upstream, has my local copy drifted, was the drift local or upstream, and what should I do next? Everything here is local-first and preview-first — nothing writes artifact files, and nothing touches the network unless you explicitly enable it.
What “upstream provenance” means
Section titled “What “upstream provenance” means”An artifact’s upstream is the source it was installed from — a GitHub repo
- path, an npm/PyPI package, a Docker image, or an MCP registry entry. Quartermaster records this as an upstream source plus a link tying it to the local item, with the installed ref/version/commit and a baseline tree hash.
Provenance auto-links at scan time: artifacts inside git clones link to the clone’s remote, plugin-cache bundles link to their marketplace clone’s remote, and items with no breadcrumb inherit the origin of identical-content siblings. The first-seen tree becomes the drift baseline.
Sidecars (.quartermaster-origin.json)
Section titled “Sidecars (.quartermaster-origin.json)”The most authoritative provenance signal is a sidecar file next to the artifact:
{ "schema": "quartermaster-origin/v1", "source": { "kind": "github_repo", "repo": "acme/pdf-demo", "path": "skills/pdf-demo", "ref": "v1.0.0", "commit": "<sha>" }, "installed_files": {}, "installed_at": "2026-06-01T00:00:00Z"}A sidecar yields a confidence: 1.0 candidate. Without one, Quartermaster
falls back to git origin, package/MCP config source, public-catalog matches,
and content fingerprints.
Finding & linking upstream
Section titled “Finding & linking upstream”find_upstream({ itemId })— rankedcandidates[](highest confidence first), each withmatchMethod,confidence, and evidence. Confidence tiers: sidecar1.00, git remote+path+commit0.98, exact tree hash0.95, repo URL0.90, primary-file hash0.85, catalog name0.80, normalized-Markdown0.75, package name0.65, known-path0.50, fuzzy name0.40.link_upstream({ itemId, candidate })— persists a selected candidate as the artifact’s upstream link. DB metadata only; no files are written.
Checking for updates
Section titled “Checking for updates”check_updates({ itemId | all, allowNetwork?, refreshSnapshot?, limit? })
compares each linked item’s installed version/commit to the latest upstream.
Cache-only by default (network: "disabled"); with ALLOW_NETWORK_UPDATES=1
and allowNetwork: true it resolves the latest from GitHub/npm/PyPI and
(with refreshSnapshot) stores an upstream-latest snapshot. Each row reports
status (current · update_available · unknown · source_missing ·
source_moved), pinned, warnings, and evidence.
Verification is git-native and cheap: local snapshots carry git blob
hashes, so upstream comparison uses one GitHub tree-listing call per repo — no
file downloads. Latest-commit lookups use git ls-remote (any git host), and
plugin bundles verify offline against the local marketplace clone.
Drift statuses
Section titled “Drift statuses”explain_drift({ itemId, persist? }) returns the full status taxonomy,
file-level changes, conflicts, semantic findings, risk, and a recommendation:
| status | meaning |
|---|---|
current_clean | current matches the latest (or the baseline when no latest is known) |
outdated_clean | no local edits; the latest upstream applies cleanly |
patched_current | local edits, no newer upstream |
patched_outdated | local edits and a newer upstream, no file conflict |
forked | the same file changed locally and upstream |
unknown_origin | no baseline yet — link an upstream first |
source_missing / source_moved | the upstream source is gone or relocated |
suspicious_drift | a high-risk semantic change (below) overrides the above |
scan_drift({ itemIds | allLinked, persist? }) computes (and by default
persists) reports for many linked items at once; they appear at GET /api/drift and the dashboard’s Drift tab.
Semantic findings
Section titled “Semantic findings”Beyond file hashes, Quartermaster flags meaningful changes:
allowed_tools_expanded, script_added / script_modified,
mcp_command_changed, mcp_source_ref_changed,
mcp_destructive_tool_added, local_path_introduced,
secret_like_token_introduced. Any high-severity finding (or a locally
modified script/config) classifies the artifact as suspicious_drift. Secrets
and local paths in the evidence are redacted before storage or display.
Pinning
Section titled “Pinning”pin_version({ itemId, upstreamSourceId, ref?|version?|commit?, reason? })sets the link’spinnedflag and records the reason. For an unpinned MCP server, the response includes a suggested pinned config preview — Quartermaster never rewrites MCP config files.unpin_versionclears the flag.
A pinned artifact still reports update_available, but check_updates and
preview_update recommend staying pinned until you unpin.
Previewing an update
Section titled “Previewing an update”preview_update({ itemId, persist? }) is the canonical dry-run merge
planner: baseline → current → latest → rich drift → operations (add_file ·
replace_file · remove_file · manual_merge · preserve_local) → policy
findings. It always returns dryRun: true, writes: []. Script changes and
conflicts become manual_merge.
Walkthrough (network-free)
Section titled “Walkthrough (network-free)”The repo ships a repeatable end-to-end demo:
docs/UPSTREAM_DRIFT_DEMO.md,
with fixtures in test/fixtures/upstream-demo/ — three views of one skill
(installed baseline, locally edited copy with a sidecar, newer upstream):
reindex # discover the local pdf-demo skillfind_upstream # sidecar is the top candidate (confidence 1.0)link_upstream # persist the linkcheck_updates # v1.0.0 vs v1.1.0 -> update_available (cache-only)explain_drift # -> patched_outdated (local SKILL.md edit + upstream README.md change)preview_update # replace_file README.md + preserve_local SKILL.md (dry-run)scan_drift # persist reports -> dashboard Drift tabThe automated version is test/e2e-upstream-drift.test.mjs (runs with
npm test, no network at any step).