agentleFS
garfiecgarfiec/Librechat-Mobile/.claude/skills/sync-upstream/SKILL.md

sync-upstream

Skill149 starsChanged 7 days agoApache-2.0View on GitHub

Sync the Switchboard client with a newer official LibreChat server version — a stable release, a release candidate, or a PARTIAL sync up to an untagged upstream commit (e.g. current dev HEAD). If the client is ALREADY at the target commit, runs an AUDIT of the last synced delta to catch anything that was missed. Coordinates two dynamic workflows (discovery, implementation) around human decision gates.

  • Commits and pushes

15 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use start-mission, tdd and 1 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • parkchuSkill

    start-mission

    넥스트스텝(NextStep) 미션을 처음 시작할 때, 미션 저장소를 fork·clone하고 최초 작업 브랜치(step1 등)까지 만들어 준다. gh CLI로 fork하고 clone까지 이어서 처리하며, gh가 없거나 인증이 안 돼 있으면 브라우저 Fork 안내로 폴백한다. 이미 fork/clone돼 있으면 재사용하고 이어서 진행한다(재실행 안전). 사용자가 "미션 시작하려는데 저장소 세팅해줘", "새 미션 시작할래", "fork부터 해줘", "/nextstep:start-mission" 등으로 요청할 때 사용한다.

    Also in 3 projects that use this

  • GoogleSkill

    tdd

    Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.

    Also in 3 projects that use this

  • MicrosoftSkill

    handoff

    Compact the current conversation into a handoff document for another agent to pick up.

    Also in 3 projects that use this

The file

We split this skill into the rules it gives an agent

5 rules. 0 are common, 1 is distinctive, and 4 are very specific to this project.

  • 0

    Common

    Most of the 11 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Always: submodule HEAD must equal commit=.

    line 76
  2. Very specific:

    Detect mode: If {target_commit} == commit= (submodule HEAD) or $ARGUMENTS contains audit → audit mode.

    line 86
  3. Distinctive:

    If sync mode and no newer tags AND branch heads == base → tell the user "already up to date" and offer an audit instead of stopping.

    line 89
  4. Very specific:

    If a prior run's artifacts exist (ls .claude/sync-upstream/artifacts/), offer to resume from them.

    line 136
  5. Very specific:

    If buildGreen is false or unresolvedConfirmed is true, report it and route back into WF2 (scoped to the failures) rather than proceeding.

    line 218

“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Sync Upstream

Synchronize the Switchboard app with a newer version of the official LibreChat server.

You are the coordinator. You run the lightweight deterministic work yourself with Bash (prereqs, target/mode resolution, final consistency asserts) and own the three human decision points. All heavy parallel work is delegated to two dynamic workflows — you never read or write app code yourself.

[you]  0. Prereqs + resolve target + detect mode          (Bash — fail loud on ambiguity)
  │
[WF1]  1. sync-upstream-discovery   — parallel multi-angle sweep → converged findings
  │
[you]  2. HITL INTERVIEW            — the one real gate: target + scope + direction   (AskUserQuestion)
  │
[WF2]  3. sync-upstream-implement   — implement → build+consistency gates → ≥2 independent review passes
  │
[you]  4. TEST + PUSH TAIL          — present test plan, facilitate device testing, hold the push gate

Two workflows do the work; you hold the seams. A workflow cannot pause to ask the user — that is exactly why the target choice, the proposal approval, and the no-push/device-test boundary live in your main loop between invocations, never inside a script.

Two modes

Resolve these before anything else (Phase 0), because they change the whole run:

ModeTriggerGoalVersion bookkeeping
syncresolved target commit ≠ current pinabsorb new upstream changesadvances (version.properties, UPSTREAM_VERSION, submodule, commit map)
auditresolved target commit == current pin (or audit in $ARGUMENTS)re-check that the LAST synced delta was fully/correctly absorbed — find missesnone — the pin does not move

Audit's default range is the last synced delta (previous pin → current pin), recovered from UPSTREAM_VERSION's own git history. --full widens it to full-pin reconciliation.

Target Modes (sync)

ModeTarget looks likebackendTargetVersionUPSTREAM_VERSIONGating strategy
releasev0.8.8 (default: latest stable tag)0.8.8tag=v0.8.8, commit=<tag commit>isCompatibleOrNewer(version, "0.8.8-rc1")
rcv0.8.8-rc10.8.8-rc1tag=v0.8.8-rc1, commit=<tag commit>isCompatibleOrNewer(version, "0.8.8-rc1")
partialdev, main, or a SHA<base>+dev.<sha8>tag=<last v-tag at/before commit>, commit=<target SHA>supportsFeature(detected, "<next-rc-line>", landedDate)

Shared invariants: the diff base is always commit= from UPSTREAM_VERSION (never tag=); BackendVersion.parse() handles all three forms; a later sync from a partial baseline is normal.


Phase 0 — Prereqs, target & mode (you, with Bash)

Working directory is already the mobile repo root — do not cd elsewhere; rely on relative paths. Fail LOUD on any ambiguity below — never guess a baseline or target for the user.

  1. Submodule. git submodule status. If upstream/ missing: git submodule add https://github.com/LibreChat-AI/LibreChat.git upstream && git submodule update --init.
  2. Version file. cat UPSTREAM_VERSION. If missing, create from version.properties backendTargetVersion + submodule HEAD (tag=v<version>, commit=<HEAD>, date=$(date +%Y-%m-%d)).
  3. Clean tree. git status --porcelain. If dirty → ask the user to commit/stash; wait.
  4. Baseline consistency (compute, then apply — a partial/rc baseline is NOT an inconsistency):
    • X.Y.Z+dev.<sha8> → <sha8> must be a prefix of commit=; verify the sha, not tag equality.
    • X.Y.Z-rcN / plain X.Y.Z → must equal tag= without v.
    • Always: submodule HEAD must equal commit=.
    • On failure → ask the user which state is the true baseline; wait. Never auto-pick.
  5. Fetch + resolve target. cd upstream && git fetch origin --tags && git fetch origin dev main. Resolve $ARGUMENTS (see Target Modes) to a full {target_commit}:
    • none → newest stable tag (exclude -rc/-beta/-alpha), but still enumerate newer rc tags and how far dev/main are ahead (git rev-list --count {base_commit}..origin/dev) for the interview.
    • tag (stable or rc) → that tag; never silently drop an rc the user named.
    • branch/SHA → partial; {base_version} from git show {sha}:package.json, {anchor_tag} = git describe --tags --abbrev=0 --match "v[0-9]*" {sha} (--match is required — upstream also tags Helm charts like chart-2.0.7).
  6. Detect mode. If {target_commit} == commit= (submodule HEAD) or $ARGUMENTS contains audit → audit mode; the audit base is the previous commit= from git log -p --format=%H -- UPSTREAM_VERSION (or full-pin if --full). Else sync mode. If sync mode and no newer tags AND branch heads == base → tell the user "already up to date" and offer an audit instead of stopping.
  7. Artifacts dir. mkdir -p .claude/sync-upstream/artifacts.
  8. Mirrored constants. scripts/check-mirrors.py --from {base_commit} --to {target_commit} --diff Some upstream values are never served over the API, so the client hardcodes a copy — which providers take documents natively, which MIME types the parser extracts, which feedback reasons the write route accepts. These drift silently: nothing fails to decode, nothing errors, the app just makes worse decisions as it ages, so the ordinary diff sweep reads them as inert constant edits and defers them. Exit 1 means at least one moved; the output names the Kotlin file to reconcile. Exit 2 is not drift — it is a fatal (empty submodule, unresolvable revision, bad registry); fix the setup and re-run rather than reporting it as a finding. Carry every DRIFT and ?? row into the Phase 2 interview as its own item — a ?? means upstream renamed or deleted the symbol, so the registry entry is wrong either way. The artifact-* rows are a different kind of mirror: what a React artifact may import (web's Sandpack dependencies, the shadcn components and their mount paths, the libraries the artifact prompt advertises). The client side is vendored JavaScript, not Kotlin, so they are reconciled with /update-web-assets (re-pin in scripts/artifact-modules/package.json, move the shadcn source URLs to the new commit, --sync), not by WF2's implementers. Also move those URLs when UPSTREAM_VERSION moves, even if the rows are ok: the shadcn-ui bundle's version is the upstream commit its sources came from. See reference/upstream-paths.md, "Artifact runtime dependencies". Registry: scripts/mirrors.json. Adding a mirror to the codebase means adding an entry there in the same PR.
  9. Client transport contract. packages/chat/src/core/ (@librechat/chat, upstream #16964) is upstream's typed ChatTransport (types.ts: send/reconnect/abort/status/resume/steer/queue, result variants, refusal codes, generationProtocolVersion) plus its reference SSE frame parser (transport/) and stream reducer (reducer.ts, steps/). It is the closest thing upstream has to a written spec of the streaming protocol this client implements, so diff it before anything else reads the range:

    cd upstream
    git cat-file -e {base_commit}:packages/chat/src/core/types.ts 2>/dev/null && echo base
    git cat-file -e {target_commit}:packages/chat/src/core/types.ts 2>/dev/null && echo target
    git diff {base_commit}..{target_commit} -- packages/chat/src/core/ \
      > ../.claude/sync-upstream/artifacts/contract-diff.patch

    Set contractState:

    • absent — in neither commit. The range predates the package (landed 2026-10-10); continue.
    • introduced — only in target. Every capability in the file is a candidate gap, not just a diff.
    • changed — non-empty patch, otherwise. If types.ts vanished from target, the contract moved: find where with git log --diff-filter=R --stat before passing it on.
    • unchanged — in both, empty patch.

    Watch the directory, not one file — the package is still landing as a PR stack and may split it. Pass contractState and the patch's absolute path into WF1, and carry its findings into the Phase 2 interview as their own group.

If a prior run's artifacts exist (ls .claude/sync-upstream/artifacts/), offer to resume from them.


Phase 1 — Discovery (WF1)

Invoke the discovery workflow. It runs the parallel multi-angle sweep, converges + cross-references the mobile codebase, and runs a completeness critic. Watch its phase tree live via /workflows.

Workflow({ name: "sync-upstream-discovery", args: {
  mode, baseCommit, targetCommit, targetMode, baseVersion, anchorTag,
  skillDir: "<abs path to .claude/skills/sync-upstream>",
  artifactsDir: "<abs path to .claude/sync-upstream/artifacts>",
  contractState,           // Phase 0 step 9
  contractDiffPath: "<abs path to artifacts/contract-diff.patch>",
} })

It returns { mode, range, findings[], counts, criticComplete, reportPath } and writes artifacts/discovery-report.md. Each finding is machine-complete: category, severity, mobile-gap status, iosMain coverage, landing commit + UTC landedDate, first-target-line, gate form. Do not paraphrase-and-lose those — thread the structured objects straight into WF2.


Phase 2 — HITL interview (you)

This is the single human gate. Present the converged report, then use AskUserQuestion to settle:

  • Target / scope (sync): confirm the resolved target or switch (stable / rc / partial — the findings are already tagged by target-line, so switching just filters). If the user switches, you have the data; no re-run needed unless they name a target outside the swept superset (then re-invoke WF1).
  • Which items to implement now vs defer (Breaking + Security cannot be deferred — say so).
  • Direction on UI items that don't map cleanly to Compose (surface the 2-3 options per item).
  • Audit: which surfaced misses to fix now.
  • Drifted mirrors (Phase 0 step 8): each one is a hand-maintained copy that upstream has moved underneath. They are cheap to reconcile and invisible if skipped — present them explicitly rather than folding them into the general findings list.
  • Artifact dependencies (the artifact-* mirror rows): name each package web added, dropped or re-pinned, and for each one added, its last release date and any advisories (the dependency policy applies). Ask per package: bundle it, bundle it behind the consent prompt (an advisory in its registry entry), or leave it unbundled (artifacts importing it show "not bundled"). Run /update-web-assets for the approved ones after WF2, on the same branch.
  • Transport contract (Phase 0 step 9): findings that trace to packages/chat/src/core/, grouped by the ChatTransport member they touch. When contractState is introduced, say plainly which capabilities the client has no equivalent for yet.
  • Items marked backend-blocked are shown as knowingly-skipped, never built.

Persist the approved, scoped list to artifacts/approved-{target}.md. If the user cancels, archive the report and stop cleanly. Do not invoke WF2 until the user approves.


Phase 3 — Implementation (WF2)

Invoke the implementation workflow with the approved list. It implements per group, runs build + consistency gates, then ≥2 mandatory independent review passes (stateless ⇒ independent by construction; CONFIRMED vs PLAUSIBLE; iteration-capped). It ends at local commits — it never pushes.

Workflow({ name: "sync-upstream-implement", args: {
  approvedItems, mode, targetMode, targetCommit, baseVersion, anchorTag,
  targetTag,               // release/rc only; null for partial
  sha8,                    // partial only
  bumpVersion,             // FALSE in audit mode
  branchName,              // chore/sync-upstream-{tag} | -dev-{sha8} | -audit-{sha8}
  skillDir, artifactsDir,
  reportPath,              // artifacts/discovery-report.md
} })

Returns { branchName, buildResults, buildGreen, consistency, reviewPasses, reviewClean, unresolvedConfirmed, testPlan }.

Then re-assert independently (you, with Bash) — do not trust the workflow's self-report blindly:

  • sync: sha8 is a prefix of commit=; cd upstream && git rev-parse HEAD == commit=; BackendCommitMap.kt contains the 12-char prefix of commit= (same check release.yml runs); README upper bound matches; each supportsFeature row's landedDate ≤ synced commit's UTC date.
  • audit: the pin did not move — commit=, backendTargetVersion, submodule HEAD, and BackendCommitMap.kt are all unchanged from before the audit.

If buildGreen is false or unresolvedConfirmed is true, report it and route back into WF2 (scoped to the failures) rather than proceeding.


Phase 4 — Test & push tail (you) — HARD STOP

Phase 3 ends at local commits. DO NOT push. DO NOT open a PR. Build + detekt + tests confirm scaffolding, not feature correctness. A sync must be device-tested against a real backend on BOTH Android and iOS (incl. older-server compat) before a PR is offered. The branch stays local-only until the user walks the test plan and explicitly says "push it" / "open the PR".

Present:

## Sync Ready for Device Testing: {baseline} → {target}   [{mode}]
Branch `{branchName}` is local-only. Not pushed. No PR opened.

### Files changed        {grouped by module}
### Test plan            {WF2 testPlan — Android / iOS / compat}
### How to build & run   Android: assembleDebug + install. iOS: linkDebugFrameworkIosSimulatorArm64, run in Xcode.
                         Connect to a target-version server + an older supported server; walk the plan.
                         Partial: verify new features HIDDEN on the older server, visible on a server the
                         regenerated BackendCommitMap COVERS (between landing and the pin). A server NEWER
                         than the pin resolves to no-version → features fail closed by design (not a bug),
                         and your own dev server drifts past the pin within days.
### Version updates      {sync: the bumps; audit: "none — pin unchanged"}

Then:

  • User reports bugs → re-invoke WF2 scoped to just those fixes (same branch), loop until they're happy.
  • User explicitly authorizes the push → only then run git push / gh pr create. Not before.

Dispatch on-device test facilitation as needed (split by platform, or platform × feature area for large syncs). Device testing is the human's; you facilitate and route blockers back into WF2.


Reference files (read by the workflows' agents, not by you)

${CLAUDE_SKILL_DIR}/reference/: upstream-paths.md (diff surface), api-mapping.md, model-mapping.md, ui-mapping.md, upstream-backend-gaps.md (client-only vapor features — do not build), android-architecture.md (implementer patterns). Root: DISCOVERY.md, VERSION_GATES.md, BackendVersion.kt, scripts/mirrors.json (hand-mirrored upstream constants — see Phase 0 step 8).

Error recovery

SituationRecovery
Submodule / UPSTREAM_VERSION missingRecreate (Phase 0 steps 1–2)
Dirty tree / baseline inconsistency / SHA not foundAsk the user — never auto-resolve
Already up to dateOffer an audit (don't just stop)
No newer tags but dev/main aheadOffer a partial sync
WF1/WF2 returns partial (agent died)Re-invoke with resumeFromRunId (cached agents replay); artifacts under artifacts/ show the phase reached
BackendCommitMap missing the pin (release.yml net)WF2's regen step runs AFTER submodule checkout; re-run if needed
Build/consistency/review failReported in WF2's return; re-invoke WF2 scoped to the failures
Session compacted mid-runArtifacts dir is the resume authority; resumeFromRunId only within a single workflow invocation

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.