news 2026/9/8 17:13:27

career-ops 开源贡献实战指南:从首个 good-first-issue 到维护者的协作全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
career-ops 开源贡献实战指南:从首个 good-first-issue 到维护者的协作全流程

career-ops 开源贡献实战指南:从首个 good-first-issue 到维护者的协作全流程

【免费下载链接】career-opsOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)项目地址: https://gitcode.com/GitHub_Trending/ca/career-ops

career-ops 是一个本地优先、人机协作的 AI 求职开源工具:它扫描招聘网站、把职位评估为结构化的 A–H 报告并给出 1–5 分评分、为你量身定制简历并跟踪申请,全部在你的 AI 编码 CLI(Claude Code、Codex、OpenCode 等)中本地运行。本文以仓库根目录的 CONTRIBUTING.md 为骨架,系统拆解该项目的贡献协作流程——包括 PR 提交流程、核心与插件层(Plugin)的架构边界、"Source Indexing Policy(数据源索引政策)"的五条硬性规则、测试目录规范与本地开发命令,并结合源码与你手把手上手第一个合并的 PR。

一、为什么在 career-ops 做第一次开源贡献

项目文档给出了几个非常实在的理由,值得先建立共识:

  • 你已经天然理解问题域。career-ops 是一个求职工具——如果你正在找工作,你比多数人更懂它的痛点,也天然是更好的贡献者。
  • 你的代码会被真实的人使用。贡献被合并后,你的名字进入的是一个真实项目的历史,而不是玩具仓库(仓库文档记载该仓库已在 GitHub Trending 长期上榜、Star 数超过 55K)。
  • 响应快。文档承诺"开 issue 或 PR 通常一两天内就有回音,没有黑洞"。
  • 有极小的上坡路径(tiny on-ramps)good first issue都被切成小块:带时间预估、可复制的模式、明确的"完成"定义,第一次 PR 是稳赢而不是迷宫。
  • 有真实的 Code Review。每个 PR 都会被人工阅读,不淹没在 bot 噪声里,也不合并"AI 垃圾"。
  • 有明确的晋升路径。持续高质量贡献者会得到公开署名,并被邀请承担更大角色(reviewer → maintainer)。

注意这些描述均来自 CONTRIBUTING.md 原文与仓库自身文档,Star 数与增长数据仅作为仓库文档陈述引用,不代表对当前时点的实时核验。

二、提交 PR 之前:先对方向,再写代码

Feature 类变更先开 issue

对于新功能、新的 mode(模式)或命令、架构级变更,文档要求先开 issue 讨论。原因是避免你在一个"最终会被我们转向"的方向上投入时间,让大家在写代码之前先对齐方向。

无需 issue、可直接 PR 的类别

以下类别直通 PR 完全欢迎、不需要先开 issue,因为流程不应拖慢它们,且这正是项目最想要的贡献:

  • Bug 修复;
  • 新的 zero-auth 扫描器 provider(无需鉴权即可读取公开职位数据的接入模块);
  • 文档;
  • 翻译。

文档同时提醒:一个绕过此步的大型 feature PR,如果不符合架构或路线图,可能被要求"先回到 issue"——这是范围对话(scope conversation),而不是对你工作质量的否定。

好 PR 的标准

  • 修复了 Issues 里列出的 bug;
  • 解决了经过讨论并获批的功能请求;
  • 包含"改了什么、为什么改"的清晰描述;
  • 遵循现有代码风格与项目哲学:简单、最小、质量优先于数量(simple, minimal, quality over quantity)。

Quick Start 七步走

  1. 开 issue 讨论你的想法;
  2. Fork 仓库;
  3. 建分支:git checkout -b feature/my-feature
  4. 做改动;
  5. 用全新 clone 测试(参见 docs/SETUP.md);
  6. Commit 并 push;
  7. 开 Pull Request 并引用该 issue。

第 5 步强调"fresh clone"很有深意:career-ops 是本地运行工具,真实环境依赖(Node >= 18、Chromium/Playwright、dashboard 的 Go 工具链,见 package.json)只有在干净环境里跑通才可靠。

三、贡献什么:好点子分级与认领机制

入门级贡献

贡献方向落点
为职位门户表补充公司templates/portals.example.yml
把 modes 翻译成其他语言modes/下各语言目录(zh/es/de/fr/等)
改进文档docs/README.*.md
为不同岗位添加示例简历examples/
报 bug仓库 Issues

更大规模的贡献

  • 新的评估维度或评分逻辑(对应 A–H 报告与 1–5 评分体系);
  • Dashboard TUI 功能(代码在 dashboard/,是一个独立的 Go 模块,含go.mod/go.sum/main.go与平台相关的open_*.go);
  • 新的技能模式(skill modes)(在 modes/);
  • 脚本改进(各种.mjs工具,根目录下即可见大量入口)。

/assign认领机制如何保持公平

对任意good first issue评论/assign即可认领,无需等待维护者。公平性由三条规则保证:

  1. 认领会自动释放:7 天无动静后(第 3 天会收到友好提醒)该 issue 回到可认领窗口,不会卡死。/extend无理由重启计时,/unassign干净放手;只要挂着打开的 PR 就暂停计时
  2. 为新贡献者预留:good-first-issue 面向在此仓库合并少于 3 个 PR 的人(first-timers-only标签则严格要求是"第一个"),一次只能认领一个,确保首次贡献者永远有路可走。超过该阶段后,help wanted标签是你的主战场。
  3. 认领不是贡献的前提:直接对任何未分配 issue 提 PR 永远欢迎。

四、贡献者阶梯:公开、可预期的晋升路径

项目有明确梯队,且"公开署名、主动邀请":

  1. 首次贡献者(First-time contributor)——你合并了一个 PR,欢迎加入;
  2. 可信贡献者(Trusted contributor)——几个扎实的合并后,你的 PR 会走快速通道,并在相关工作里 @ 你;
  3. 审阅者(Reviewer)——帮助分流与审阅他人 PR(由项目方邀请);
  4. 维护者(Maintainer)——参与掌舵项目方向。

想多做一点?直接在 issue 里说出来即可。

五、接管被放弃的 PR:public、可预测的三级阶梯

"生活会发生:一个 PR 收到 review,作者转行了,有用的工作停在 80% 完成。"career-ops 的做法不是让 bot 埋掉它,也不是任它腐烂,而是走一条公开且可预测的阶梯:

  1. Review 一轮后静默两周,维护者将该 PR 选入接管流程(adoption/track),并友好提醒原作者:"还是你的,不急。"
  2. 再过两周,进行第二次确认,并写明后续计划:若再静默两周,工作将开放认领。
  3. 只有到这一步,维护者(绝不通过 bot)才会感谢并关闭原 PR,同时开一个标记为adoptable的伴生 issue,指向原分支并逐条列出剩余工作。

接管一个被放弃的 PR 是最有价值的首次贡献之一:diff 基本完成、review 已经写好、剩余工作范围明确。做法是开一个新 PR并携带原始 commits(git 会保留作者署名),或用Co-authored-by:尾部追加原文作者。两位贡献者都得到署名:原作者署名工作,你署名落地。

如果你是回来的原作者:只要还没被别人完成,工作随时可收回——在 issue 上说一声即可;阶梯任何一步上你的一次评论或 push 都会完整重置计时

六、"别人的开放 PR 仍然是别人的":边界与自动化规则

上述接管阶梯只针对已被放弃的工作。一个作者仍在活跃的开放 PR 是另一回事,这条线非常明确:

  • 不要开一个"重新解决别人冲突"的 PR。在评论区指出某 PR 已冲突确实有用;但把它 rebase 到自己的分支再开替代 PR 不行——因为替代 PR 合并会关闭原 PR,原作者得到的将是closed而不是本该属于他的merged那个徽章是贡献者从这个项目带走的大部分东西,不归我们重新分配。
  • 如果冲突源自项目方自己的合并,修复责任在项目方:维护者会在作者自己的分支上解决冲突(这正是 "Allow edits by maintainers" 的意义)、跑全套测试、保留原 PR 与作者身份原封不动。冲突来自其他任何地方,则由作者在自己方便时 rebase——"没人被催着赶这个时间表"。
  • 自动化同样受约束:bot 在别人分支上开替代 PR 等于以更高音量做同一件事;在别人线程里贴自动化分流意见("别两个都合并""把 #X 当作主 PR")会读起来像项目决策。合并决定只能由维护者做出。自动化 Agent 只能在自己 operator 开的 PR 上评论;在他人 PR 上的自动化评论会被当作离题内容最小化。你以自己名义手写的 Review,在任何 PR 上都欢迎。
  • 超出"解决冲突"的改进欢迎,但不要钉在别人 PR 上:在讨论串里提出、让作者决定,或等它合并后开自己的 PR。

七、架构边界:core 与共享层(plugin-first)怎么分

career-ops core 的定位是local-first(本地优先)与 human-in-the-loop(人在回路):它跑在你的机器上,起草的申请材料由审查并提交。而集中式基础设施——托管职位聚合、共享匹配服务、代理或 Workers——不属于 core,那是更重的、未来作为独立、opt-in 服务的方向。

动手之前的经验法则:provider 模块、语言、CLI 支持、core 主路径上的 modes、dashboard、文档与修复 → 属于 core。更大的集中化/自动化想法(托管层、auto-apply、爬取基础设施)→ 先在方向讨论里发起,而不是交一个无法合并的大型 PR。

平行功能四问(the parallel-feature test)

career-ops 对很多事说"是":provider、语言、CLI 支持、修复。它刻意挑剔的是平行功能——那些与求职主路径相邻、单独看也各自有用的东西。因为每个合并的 feature 都是"永远维护它"的承诺(文档、测试、agent 上下文、升级路径),所以"它写得好不好"根本不是门槛。提这类功能前,先自问四个项目自己也在用的问题:

  1. 它在核心主路径上吗?核心路径是:发现职位(discover)→ 评估(evaluate)→ 定制(tailor)→ 申请(apply)→ 跟踪(track)→ 闭环(close the loop)。服务于这条路径的基础设施(去重、原子写入、状态转换台账)即使不可见也属于核心。而在路径旁边的功能(联系人管理、日历、笔记)从 plugin 起步。
  2. 谁来付维护成本?一个把某个工作流做到极致、却给所有人增加表面积(新数据文件、新脚本、新模式)的 feature,需要被证明有真实需求(多个人的 issue,而不是一个人),否则就应该住进 plugin。
  3. Plugin-first,凭证据毕业。相邻功能先做成 plugin(契约见 docs/PLUGINS.md):你自己掌控发布节奏,项目把它登记在 registry 里。若 plugin 获得真实采用,项目会考虑把它"毕业"进 core——基于证据的晋升而非关卡,这是 WordPress 运营 feature-projects 的方式。
  4. 它符合项目的形状吗?一个破坏既有模式的 mode 或 API,会给每个未来的用户和贡献者制造认知负担,即使它工作正常。合并前请先接受"与代码库一致的拼写/写法"这一要求。

四条任一不通过,是"路由问题"而非"拒绝":先开 issue,我们会告诉你去哪扇门——core、plugin 还是独立项目。plugin registry 提供真实的分发渠道,而今天不适合 core 的想法,仍可能成为你交付的最有用的东西。

八、Source Indexing Policy:所有数据源共用的一把尺

career-ops 从公开来源读取职位:ATS、招聘板、公司招聘页、人才网络。这套政策是每个来源都必须通过的唯一标准——无论谁提议,包括提交自家招聘板的运营者。项目"不评判来源的商业模式,只评判它的数据";这五条规则是 MANIFESTO.md(CareerOps 宣言)在数据源上的落地。

  1. 索引什么:任何"职位真实、可归属到可识别的雇主、且候选者可免费阅读与申请"的来源。宣言权利第 4 条"You never pay"同样适用于来源:对职位或申请设付费墙的来源,无论其他方面如何,都不会被索引
  2. 规范 URL(Canonical URL):每条职位携带来源所暴露的、通往雇主的最短可验证路径(可用时用 ATS 或直投链接)。来源自身页面只能作为次级归属。
  3. 付费置顶到不了候选人:推广内容不能购买在 career-ops 中的位置——排序发生在每个用户自己的机器上,provider 遍历其来源的完整库存,维护者会对来源做响应偏差审计(API 总量 vs 站点总量、页面分布)。career-ops 自身不带任何赞助位。这是宣言权利第 8 条"Your agent works for you. Not for a platform, not for an employer."在数据层的强制执行。
  4. 索引不等于背书,分发也不是欠谁的:进入 registry 会把职位摆到安装用户群面前,真实、可衡量、且依赖渠道——但没有任何来源被欠着位置、流量或永久性。来源必须声明其运营者,且单一来源不得超过 registry 的 40%
  5. 聚合层属于项目:一个 provider 只读它自己的来源。跨来源的聚合、排序、匹配与 registry 都住在 core 中,绝不委托给某个来源

政策如何被逐条执行

可以读 docs/SOURCE_INDEXING_LOG.md:每个已收录来源一条记录,写清楚"检查了什么、怎么验证的"。文档特别说明该日志不是排名也不是承诺,"Verified"意味着"有人跑了命令并报告输出",而不是"声明被接受";涉及活端点检查会注明采样时间,因为线上检查会过期。

该文档里两个典型判例值得参考:

  • remotli.ch(第一位在成文政策下过审的运营者自荐来源):验证了规则 1(无候选者付费墙)、规则 2(采样 121 行,雇主 URL 占比 100%)、规则 3(运营者自曝不加remote=all只覆盖 392/921 条,合并的 provider 遍历全部 19 页)。**"披露在前,规则裁决,而不是靠对话"**正是政策存在的意义。
  • a16z speedrun 人才网络(促成政策成文的案例):listing 后由贡献者读线上 feed 发现并修复了两个覆盖缺陷(分页容量 50 vs 100、单次上游瞬时故障导致整板中断)——这正是规则 3 针对的失效模式:"看着完整、实则部分覆盖"。

怎么提出一个新来源

想提案一个来源(自己的或别人的):

  • source proposalissue 模板,逐条对照上面五条规则;或
  • 直接提交带 provider 的 PR 也欢迎,合并前同样走这五条规则。
  • 运营者声明在收录前须带外核实(out-of-band verification):一个可在来源自身域名下联系到的联系人,或等效的域名控制证明。运营者提议自己的招聘板完全没问题——"规则化门槛"正是为此而设。

从实现侧印证:一个 provider 是一个 providers/ 下的{name}.mjs模块,通过 providers/_registry.mjs 被 scan.mjs 与 verify-portals.mjs 自动加载——放入文件即完成注册,无手工登记。完整的接入契约、强制护栏与tests/providers/{name}.test.mjs必须覆盖的内容见 providers/ADDING_A_PROVIDER.md。

九、开发守则(Guidelines)

  • 尽量让 modes保持语言无关(Claude 能同时处理 EN 与 ES);
  • 脚本应优雅处理缺失文件——先existsSyncreadFileSync(这也是 tests 中大量用例覆盖的防御模式);
  • Dashboard 改动必须构建npm run build:dashboard,并用真实数据测试后再提交
  • 不要提交个人数据cv.mdprofile.ymlapplications.mdreports/属于本地用户层,不进版本库。

十、明确不接受的 PR

以下清单是项目红线,任何类别都会被主动拒绝,了解它能在动手前省下大量时间:

  1. 爬取禁止自动化访问平台的 PR(如 LinkedIn 等)——为尊重第三方 ToS;
  2. 绕过人工审查、自动提交申请的 PR——career-ops 是决策支持工具,不是 spam bot(呼应宣言:Nothing is ever auto-submitted);
  3. 未经 issue 讨论就引入外部 API 依赖的 PR
  4. 针对内置 plugin 的 feature PR(plugins/apify、plugins/gmail、plugins/notion)——内置 plugin 是稳定的reference seeds,要扩展就发布你自己的career-ops-plugin-<id>,项目会登记它为"安装后即优先"的被维护后继;内置 plugin 只接收安全/兼容性修复;
  5. 向 core 添加集中化或托管基础设施(代理、聚合服务、共享 Workers)——那属于独立 opt-in 服务而非 open-core;
  6. 以通用聚合索引作为依赖——把"整合众多来源的统一聚合层"作为第三方依赖接入;单读各招聘板的 provider 永远欢迎,统一的聚合层本身必须是一方(first-party)
  7. 把数据发给第三方服务的集成——需要第三方账号、或把简历/流水线/笔记推到外部服务的 provider 或同步功能。career-ops 本地优先、零密钥:求职数据留在你的机器上。本地读取公开职位 API 完全欢迎(内置 provider 就是这么工作的),把个人数据路由给别人的服务则不行;
  8. 主要消费者是第三方产品的集成——调用方是别人家产品(bot、SaaS、外部编排器)的模块/契约/适配器,即便代码本身通用,也属于 plugin 或独立项目,绝不属于 core;项目自身一方表面(官方 web 体验、可选的共享服务)例外;
  9. 向 README 添加第三方托管的入口或服务徽章——README 只保留项目控制的资产;基于 career-ops 构建的项目欢迎在社区里分享,但不能上首页;
  10. 包含个人数据的 PR(真实简历、邮箱、电话)——请用 examples/ 下的虚构数据。

十一、本地开发与测试规范(实测可复制)

仓库文档给出的一套核心开发命令,与 package.json 的 scripts 一一对应:

# 脚本/自检 npm run doctor # 设置与本地环境校验(对应根目录 doctor.mjs) node verify-pipeline.mjs # 流水线健康检查 node cv-sync-check.mjs # 配置一致性检查(简历相关配置同步) # Dashboard(Go TUI) npm run build:dashboard # 平台正确的 go build(调用根目录 build-dashboard.mjs) npm run serve:dashboard # 以仓库根目录为 --path 启动 TUI # 测试 node test-all.mjs # 全套测试 —— push / 开 PR 前必跑 node test-all.mjs --quick # 全套但跳过 dashboard 构建 node test-all.mjs --only providers/themuse # 只跑某个 provider 的测试

新测试必须独立成文件:tests/ 的自动发现机制

任何新测试都应放在tests/下自己的文件里,而不是作为test-all.mjs里的编号小节。凡是匹配tests/**/*.test.mjs的文件都会被自动发现——无需注册、无需选编号

从源码看这不是洁癖,而是真实的协作教训:见 test-all.mjs 头注——2026 年 8 月六个贡献者同时往test-all.mjs末尾加编号小节,六个人不约而同都选了60a,每个合并都迫使其余五个人 rebase——"六行测试代码换来约十五次 rebase 和六次串行化的 CI 运行"。而一个新文件不与任何人冲突,这些 PR 可以全部并行落地。实现上 test-all.mjs 的discoverTests()readdirSync+ 字典序确定性遍历,还会跳过嵌套 checkout(worktree)防止误执行。

新增扫描 provider?读这份完整契约

见 providers/ADDING_A_PROVIDER.md:完整契约、强制护栏、以及tests/providers/{name}.test.mjs必须覆盖什么(例如禁止process.exit()、必须用测试助手计数、健康探测时的行为等,实际仓库的tests/providers/下已沉淀 100+ 个对应测试文件)。

Web 端测试布局

Web 套件位于 web/tests/,镜像被测模块在web/src/下的路径(如src/lib/clean-chips.mjstests/lib/clean-chips.test.mjs),命名为{module}.test.mjs。要点:

  • web/自己的npm test通过 glob 发现它们,同样无需注册;
  • 不要让它们落在web/src/——Next.js 会扫描该目录树;
  • 写成.mjs——node --test没有 TypeScript loader;
  • 布局细节见 web/README.md,并由根套件的 tests/web-test-layout.test.mjs 在每个 PR上强制校验。

--only只是开发便利,不是 PR 门槛

test-all.mjs 头部用大写警告写得很直白:--only只跑匹配到的tests/文件,跳过所有内联核心小节(语法、脚本、dashboard、数据契约、个人数据、路径等)。因此--only全绿 ≠ 全套通过——push 前永远要跑完整的node test-all.mjs。配套实现中,test-all.mjs还会对"无匹配文件"直接process.exit(1),让路径拼写错误永远不会悄悄把 CI 变绿。

十二、品牌、商标与许可

  • 代码贡献受MITLICENSE 管辖;
  • "career-ops" 名称本身受 TRADEMARK.md 管辖;
  • 若你 fork 做商业用途:MIT 允许,但请给产品起你自己的名字,并遵循商标政策中关于商业命名与背书声明(endorsement claims)的规定。

结语:从文档到合并的完整闭环

最后把整条路径串起来:想方向(feature 先开 issue)→ 认领或自选(/assign,或直接对未分配 issue 提 PR)→ 判断归属(core / plugin / 独立项目,过"平行功能四问")→ 涉及新数据源先过 Source Indexing Policy 五条规则 → 遵守开发守则与红线清单 → 本地跑node test-all.mjs全绿(新测试独立成.test.mjs文件)→ 提交 PR → 通过人类 Review → 合并 → 走上贡献者阶梯

如果遇到困惑,最有效的入口都在仓库内:开 issue、通读 docs/ARCHITECTURE.md 理解分层、对照 docs/SOURCE_INDEXING_LOG.md 看政策如何落地,或用全新的 clone 按 docs/SETUP.md 从头跑一遍再开始改代码——这既是测试你的环境,也是测试你即将贡献的项目的真实安装体验。

【免费下载链接】career-opsOpen-source AI job search: scan job portals, evaluate listings into a structured A-H report with a global 1-5 score, tailor your CV, track applications — runs locally in your AI coding CLI (Claude Code, Codex, OpenCode, Antigravity…)项目地址: https://gitcode.com/GitHub_Trending/ca/career-ops

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 17:12:52

DeepSeek Harness 不是银弹:一切皆插件背后的工程账

​摘要​&#xff1a;DeepSeek Harness 更像 Agent runtime 基础设施&#xff0c;不像现成办公软件。它把 Model Adapter、Tool Registry、Session Log、Agent Loop、调度、存储和 UI 都做成可替换插件&#xff0c;适合研究和定制。采用前要算清 token、性能、调试、安全和生态…

作者头像 李华
网站建设 2026/9/8 17:10:50

pot-desktop 3 步搭起个人生词本:跨平台划词翻译工具

pot-desktop 3 步搭起个人生词本&#xff1a;跨平台划词翻译工具 【免费下载链接】pot-desktop &#x1f308;一个跨平台的划词翻译和OCR软件 | A cross-platform software for text translation and recognition. 项目地址: https://gitcode.com/GitHub_Trending/po/pot-des…

作者头像 李华
网站建设 2026/9/8 17:09:56

2026年国内ERP系统选型指南:国企、互联网与大型企业适配方案

核心摘要2026年国内ERP选型已进入“场景细分”时代。国企首重合规安全&#xff08;等保/审计&#xff09;&#xff0c;互联网企业看重弹性扩展&#xff08;云原生/API&#xff09;&#xff0c;大型企业聚焦一体化协同&#xff08;业财一体/全链路&#xff09;。本文基于吉客云、…

作者头像 李华
网站建设 2026/9/8 17:09:21

如何用 JSON 配置快速搭建后台管理系统:amis 低代码框架实战指南

如何用 JSON 配置快速搭建后台管理系统&#xff1a;amis 低代码框架实战指南 【免费下载链接】amis 前端低代码框架&#xff0c;通过 JSON 配置就能生成各种页面。 项目地址: https://gitcode.com/GitHub_Trending/am/amis 做后台管理系统&#xff0c;你大概率写过这类重…

作者头像 李华