|
| 1 | +# Upstream sync |
| 2 | + |
| 3 | +This workflow imports Kimi Code releases into Pythinker Code without changing the Pythinker product identity. `blackbox/refrence` is the read-only Kimi upstream checkout; Pythinker development and releases stay in this repository. |
| 4 | + |
| 5 | +## Brand boundary |
| 6 | + |
| 7 | +- Never merge `blackbox/refrence/main` directly into Pythinker `main`. |
| 8 | +- Import a full upstream tree through `rebrand.mjs`, commit that tree on `vendor/upstream`, then merge `vendor/upstream` into a fresh sync branch. |
| 9 | +- Keep Pythinker names, package scopes, URLs, logos, managed-service policy, and retained fork features. |
| 10 | +- Keep deliberate Kimi/Moonshot provider data: `api.moonshot.*`, `api.kimi.com`, `platform.kimi.*`, `kimi-k*` models, `kimi-for-coding`, `moonshot-*` provider IDs, and `MOONSHOT_API_KEY`. |
| 11 | +- Merge the pull request with a merge commit. Squash or rebase removes the vendor ancestry that future three-way merges need. |
| 12 | + |
| 13 | +## Import a release |
| 14 | + |
| 15 | +Run each step from the Pythinker repository root unless the command uses `git -C`. |
| 16 | + |
| 17 | +1. Fetch Kimi and select the release commit. |
| 18 | + |
| 19 | + ```sh |
| 20 | + git -C blackbox/refrence fetch origin |
| 21 | + git -C blackbox/refrence log --oneline --decorate origin/main |
| 22 | + git -C blackbox/refrence show --stat <release-commit> |
| 23 | + ``` |
| 24 | + |
| 25 | +2. Export and rebrand the selected tree. Both output locations must be disposable task-local directories because `rebrand.mjs` replaces its output directory. |
| 26 | + |
| 27 | + ```sh |
| 28 | + export_root="$(mktemp -d "${TMPDIR:-/tmp}/pythinker-upstream.XXXXXX")" |
| 29 | + rebrand_root="$(mktemp -d "${TMPDIR:-/tmp}/pythinker-rebrand.XXXXXX")" |
| 30 | + export_dir="$export_root/tree" |
| 31 | + rebrand_dir="$rebrand_root/tree" |
| 32 | + mkdir "$export_dir" |
| 33 | + git -C blackbox/refrence archive <release-commit> | tar -x -C "$export_dir" |
| 34 | + node scripts/upstream-sync/rebrand.mjs "$export_dir" "$rebrand_dir" |
| 35 | + ``` |
| 36 | + |
| 37 | +3. Replace the `vendor/upstream` snapshot in a dedicated worktree and commit it. |
| 38 | + |
| 39 | + ```sh |
| 40 | + worktree_root="$(mktemp -d "${TMPDIR:-/tmp}/pythinker-vendor.XXXXXX")" |
| 41 | + vendor_worktree="$worktree_root/worktree" |
| 42 | + git worktree add "$vendor_worktree" vendor/upstream |
| 43 | + test "$(git -C "$vendor_worktree" branch --show-current)" = "vendor/upstream" |
| 44 | + rsync --archive --delete --exclude .git "$rebrand_dir/" "$vendor_worktree/" |
| 45 | + git -C "$vendor_worktree" status --short |
| 46 | + git -C "$vendor_worktree" add -A |
| 47 | + git -C "$vendor_worktree" commit -m "vendor: rebrand Kimi Code <release>" |
| 48 | + ``` |
| 49 | + |
| 50 | +4. Create a fresh sync branch from current Pythinker `main`, then merge the vendor snapshot. Keep rerere enabled so recorded conflict resolutions replay. |
| 51 | + |
| 52 | + ```sh |
| 53 | + git switch main |
| 54 | + git pull --ff-only |
| 55 | + git switch -c sync/upstream-<release> |
| 56 | + git config rerere.enabled true |
| 57 | + git merge --no-ff vendor/upstream |
| 58 | + ``` |
| 59 | + |
| 60 | +5. Resolve only genuine new conflicts. Retain Pythinker branding and fork features; do not restore upstream managed-account behavior. |
| 61 | + |
| 62 | +## Post-merge checks |
| 63 | + |
| 64 | +Run these checks in order. |
| 65 | + |
| 66 | +1. Find silently deleted files and compare them with the vendor tree. Restore a path that exists in `vendor/upstream` unless it is an intentional Pythinker deletion. |
| 67 | + |
| 68 | + ```sh |
| 69 | + git diff --name-only ORIG_HEAD..HEAD --diff-filter=D |
| 70 | + git ls-tree -r --name-only vendor/upstream |
| 71 | + ``` |
| 72 | + |
| 73 | +2. Require zero camel-case rename residue. |
| 74 | + |
| 75 | + ```sh |
| 76 | + rg 'dynamic_workflow[A-Z]' |
| 77 | + ``` |
| 78 | + |
| 79 | +3. Audit every brand match. Only the provider values listed in [Brand boundary](#brand-boundary) can remain. |
| 80 | + |
| 81 | + ```sh |
| 82 | + rg -in 'kimi|moonshot' |
| 83 | + ``` |
| 84 | + |
| 85 | +4. Verify that upstream did not restore the removed managed service. |
| 86 | + |
| 87 | + ```sh |
| 88 | + node scripts/upstream-sync/check-managed.mjs |
| 89 | + ``` |
| 90 | + |
| 91 | +5. Run the full gates separately and record each exit status. |
| 92 | + |
| 93 | + ```sh |
| 94 | + pnpm run typecheck |
| 95 | + pnpm run lint |
| 96 | + pnpm test |
| 97 | + pnpm -C apps/vscode run typecheck |
| 98 | + pnpm -C apps/vscode test |
| 99 | + nix build .#pythinker-code |
| 100 | + node scripts/check-nix-workspace.mjs |
| 101 | + node scripts/upstream-sync/check-managed.mjs |
| 102 | + ``` |
| 103 | + |
| 104 | +If `pnpm-lock.yaml` changed, update the `pnpmDeps` hash in `flake.nix` and rerun the Nix build. Treat a full-suite failure as red until the exact failing file passes in isolation and the load-related difference is documented. |
| 105 | + |
| 106 | +## Pull request |
| 107 | + |
| 108 | +Use a Conventional Commit title, fill the pull request template, run `gen-changesets`, and run the local review CLI when the diff exceeds the hosted reviewer limit. Merge only after required checks pass and every review conversation is resolved. Use a merge commit so `main` retains `vendor/upstream` ancestry. |
| 109 | + |
| 110 | +## Known traps |
| 111 | + |
| 112 | +- 3-way merges silently delete files our history removed but upstream didn't touch — always diff vendor file list vs worktree after a merge. |
| 113 | +- Git rename detection pairs upstream `vis` with `dashboard` — gone now, but watch for similar pairings. |
| 114 | +- Prose rename rules corrupt identifiers; the camel regex (`swarm(?=[A-Z])`) must stay ahead of the snake fallback in rebrand.mjs. Vendor snapshots built before the fix still carry `dynamic_workflowX` residue — sweep after merging. |
| 115 | +- Merged package.json/vite.config lose our test tooling (jsdom, @vue/test-utils, test block) — re-check after every web merge. |
| 116 | +- Vendor tree carries managed-service code back in on every sync — the D5 strip must be rerere-recorded deletions, plus the grep gate as a backstop. |
0 commit comments