al-folio v1.x 薄 Starter 架构下的 Coding Agent 协作指南:变更路由、三大静默失败模式与验证命令集
【免费下载链接】al-folioA beautiful, simple, clean, and responsive Jekyll theme for academics项目地址: https://gitcode.com/GitHub_Trending/al/al-folio
al-foliov1.x 将仓库定位为"薄 Jekyll Starter"而非传统主题,运行时全部下沉到独立发布的 gem 中,这使得 Coding Agent 在其中的每一次改动都必须先回答"这个改动属于谁"的问题。本文以仓库根目录的 AGENTS.md(v1.x 时代的权威 Agent 入口文件)为主线,完整展开其变更路由表、Stop Sign 路径、三种不产生任何报错的静默失败模式,以及经过验证的本地命令集,并结合Gemfile、_config.yml、test/style_contract.js 与集成测试脚本给出源码级佐证。读完你既能正确判断一次改动的归属,也能用一整套可复现的命令完成自检并安全提交 PR。
一、先认清仓库本质:al-folio v1.x 是 Starter,不是主题
AGENTS.md 开篇就给出最核心的定位判断:
al-foliov1.x is athin Jekyll starter, not a theme.
这句话决定了整个仓库的目录结构和所有协作规则。所谓"薄 Starter",意味着当前仓库只拥有四类资产:
- Starter 接线(wiring):
Gemfile、_config.yml、_data/featured_plugins.yml——负责声明依赖、激活插件、登记功能开关; - 示例内容(content):
_pages、_posts、_projects、_news、_teachings、_books、_bibliography——供用户参考和修改的站点素材; - 文档(docs):
docs/下的长文指南,以及 AGENTS.md 本身(Agent 规则); - 跨插件测试(tests):
test/integration_*.sh集成测试与test/visual/视觉一致性测试。
而所有运行时——布局(layouts)、包含文件(includes)、Sass、Liquid 标签(tags)、过滤器(filters)、功能级 JavaScript——都存放在按版本独立发布到 RubyGems 的 gem 中,这些 gem 由al-org-dev组织统一开发维护。
这条边界在仓库里有大量硬证据。看 Gemfile 中的group :al_folio_plugins,每一个功能 gem 都被精确 pin 到发布版本,例如al_folio_core '= 1.0.15'、al_search '= 1.0.3'、al_math '= 1.0.2';再看 _config.yml 第 259 行,theme: al_folio_core——主题运行时由 gem 提供,而不是从本仓库的_layouts/读取。也就是说,你在这个仓库里找不到_layouts/、_includes/、_sass/这些目录,这不是遗漏,而是架构设计使然。
因此,AGENTS.md 特别强调:"编辑运行时(runtime)是这里最常见的错误。" 如果一次改动涉及 layout、include、tag、filter 或功能行为,它应该属于拥有该部分的 gem。关于各组件如何在运行时连接起来,docs/ARCHITECTURE.md 提供了权威说明。
二、变更路由表:先查表,再动手
AGENTS.md 的核心是一张"你的改动 → 应该改哪里"的路由表。这张表是 Agent 在仓库中工作的第一决策依据,必须完整理解:
| 你的改动 | 应该去的地方 |
|---|---|
| 依赖 pin、插件激活、功能开关 | 本仓库:Gemfile和_config.yml(两者都要改,原因见下文静默失败模式 2) |
| 示例/演示内容、文献库、数据文件 | 本仓库:_pages、_posts、_projects、_news、_teachings、_books、_data |
| 文档 | 本仓库:docs/(长文)或 AGENTS.md 本身(Agent 规则) |
| 跨插件集成测试、视觉一致性测试 | 本仓库:test/integration_*.sh、test/visual/ |
| 插件目录元数据 | 本仓库:_data/featured_plugins.yml |
| layout、include 或 Sass 局部文件 | 所属 gem——从al_folio_core开始 |
| Liquid 标签/过滤器,或标签的渲染结果 | 注册它的 gem——见 委派表 |
| 功能行为(搜索、数学、图表、评论、Cookie、图标、CV、distill、分析、图片、通讯订阅、引用) | 该功能的 gem——见 docs/BOUNDARIES.md |
| 组件/单元测试(gem 拥有行为的测试) | 所属 gem,而不是本仓库 |
| 尚无归属者的新功能 | 先提交插件提案 issue,再建独立插件仓库 |
docs/BOUNDARIES.md是权威的"区域 → gem"对照表,docs/ARCHITECTURE.md则解释各部件如何连接。
路由背后的委派机制:wrapper → tag → gem
为什么"功能行为"要路由到 gem?docs/ARCHITECTURE.md 给出了运行时委派机制:al_folio_core是中枢(_config.yml中theme: al_folio_core),它提供全部基础_layouts/*.liquid与_includes/*.liquid、基础主题 JS/CSS、details与file_exists标签,以及hideCustomBibtex、remove_accents过滤器;而它的_includes/plugins/*.liquid包裹器只负责把调用转发给兄弟 gem 注册的自定义标签。例如:
search assets调al_search_assets标签,由al_search拥有(Cmd-K ninja-keys 命令面板,索引在构建期从内容生成);comments调al_comments,由al_comments拥有(Giscus + Disqus,front matter 门控);math调al_math_styles/al_math_scripts,由al_math拥有(MathJax、pseudocode.js、TikZJax);layout: cv调al_folio_cv_render,由al_folio_cv拥有(RenderCV YAML + JSONResume);layout: distill调al_folio_distill_render,由al_folio_distill拥有(vendored、哈希 pin 的 distillpub 运行时);- 升级/审计 CLI
bundle exec al-folio …由al_folio_upgrade拥有。
理解这张委派链是正确路由改动的前提:你在仓库里看到的功能几乎总是由某个 gem 的标签最终渲染,直接在本仓库"修功能"会破坏边界。
三、Stop Sign:这些路径在本仓库中属于 gem
AGENTS.md 给出了一个非常直观的"停止标志"——如果某次改动会在本仓库里创建下列任何路径,它就应该放进 gem 而不是这里:
_layouts/ _includes/ _sass/ _scripts/ assets/tailwind/ tailwind.config.js assets/webfonts/这条限制由 CI 强制落地:npm run lint:style-contract会在本仓库出现上述任何路径时让构建失败,同时它也拒绝build:css/build:tailwindnpm 脚本——不要为 starter 添加本地 Tailwind 或 CSS 构建管线。
从源码看强制机制的实现
打开 test/style_contract.js 可以看到这套契约检查的实现细节:
- 禁止脚本:
package.json中若出现build:css、build:tailwind、build:tailwind:watch中的任何一个,即报错(第 14-18 行); - 必须保留的接线:
_config.yml必须包含theme: al_folio_core,plugins必须包含al_folio_core、al_folio_distill、al_cookie、al_icons,启用数学功能时必须包含al_math(第 20-38 行); - 第三方库契约:
third_party_libraries必须为fontawesome、academicons、scholar-icons定义带 SRI hash 的integrity.css,为tikzjax、tocbot定义 v1 运行时条目(第 40-54 行); - Gemfile 契约:
al_math必须 pin 到精确的已发布版本(= x.y.z),禁止使用:git =>分支 pin(第 56-66 行); - 禁止路径:逐一检查
_includes、_layouts、_sass、_scripts、assets/tailwind、tailwind.config.js、assets/webfonts是否存在(第 68-72 行),同时禁止在assets/fonts/下 vendor 图标字体产物(第 74-83 行); - 必需路径:
test/visual、test/integration_plugin_toggles.sh、test/integration_distill.sh必须存在(第 85-89 行)。
值得特别强调的是适用边界:这条限制只作用于本 starter 仓库本身。用户基于模板创建自己的站点时,可以合法地 shadow gem 拥有的文件——在用户仓库中放入同名路径(如_layouts/bib.liquid)即可覆盖 gem 版本,详见 docs/ARCHITECTURE.md。另外注意一个已知维护者事项:test/style_contract.js会随模板一起分发到每个新站点,因此用户添加合法的本地覆盖时,也可能在自己的 fork 里看到 starter 自身的契约检查失败,如何重新界定检查范围是一个尚未关闭的维护者决策。
四、三大"静默失败"模式:改了却没反应,多半是这三点
AGENTS.md 明确指出,绝大多数"我改了但什么都没发生"的报告,都源于下面三种不产生任何构建报错的情况。这也是最容易被 Agent 忽略的陷阱。
模式 1:功能静默失败——双层门控必须同时满足
功能门控是两层的,只有两层都"放行",功能才会渲染:
- 站点级配置开关(位于
_config.yml):search_enabled、enable_math、enable_cookie_consent、enable_darkmode、al_folio.features.cv.enabled、al_folio.features.distill.enabled,以及analytics:下的 provider ID。 - 页面级 front matter 显式选择:如
images:、tikzjax、chart.*、mermaid.*、giscus_comments、layout: distill、layout: cv。
机制上,al_folio_core在_includes/plugins/*.liquid中提供薄包裹器,调用兄弟 gem 定义的自定义 Liquid 标签;当所属 gem 不在插件列表里、或对应开关关闭时,标签只输出空字符串——没有警告、没有 missing-tag 错误、没有视觉占位符,功能就这样"不存在"了。
因此,排查一个"什么都没做"的功能时,按 AGENTS.md 给出的顺序依次检查:
- gem 是否同时出现在
Gemfile和_config.yml的plugins:列表中(见模式 2); - 站点级开关是否打开;
- 页面 front matter 是否显式选择该功能;
third_party_libraries中对应条目是否存在且带 SRI hash。
模式 2:Gemfile与_config.yml是两份必须一致的清单
插件激活要求在两个文件里做两处编辑:
- Gemfile 的
group :al_folio_plugins:pin 依赖,例如gem 'al_folio_core', '= 1.0.15'; - _config.yml 的
plugins:列表:Jekyll 的激活条目。
只出现在其中一个文件里的 gem 是"惰性"的:只在Gemfile里,Jekyll 不会加载它;只在plugins:里,Bundler 不会安装它。新增或移除插件,都必须同时编辑两处。另外注意拼写差异:仓库目录用连字符(al-folio-core),gem/插件 id 用下划线(al_folio_core)——对照 Gemfile 与 _config.yml 可以看到这一规律被严格遵循。
模式 3:本仓库的有效 baseurl 是/al-folio
演示站点以项目页(project page)形式发布,因此_config.yml已经设置了baseurl: /al-folio。普通构建会自动拾取它——deploy.yml、broken-links-site.yml、axe.yml运行的都是不带参数的bundle exec jekyll build。要点是有效 baseurl 必须保持/al-folio:
bundle exec jekyll build --baseurl /al-folio bundle exec jekyll serve # 访问 http://localhost:4000/al-folio/(注意路径)显式传--baseurl /al-folio是冗余但无害的;真正破坏站点的是把 baseurl 清空——用空 baseurl 构建后,所有资源与内部链接都会高出一个路径段,页面"能构建出来"却完全无样式、链接全断。Docker 入口同样在/al-folio下服务。而在你自己的站点上规则不同:个人/组织站点(username.github.io)必须让baseurl为空但保留该键;项目站点则设置baseurl: /<project-name>/。更多说明见 docs/FAQ.md。
五、验证过的本地命令集:按顺序执行的自检流水线
AGENTS.md 给出了一套在仓库根目录按顺序执行的完整命令集,是每次改动后、提交前必须跑通的验证流水线:
bundle install npm ci npm run lint:prettier npm run lint:style-contract bundle exec jekyll build --baseurl /al-folio bash test/integration_comments.sh bash test/integration_plugin_toggles.sh bash test/integration_distill.sh bash test/integration_bootstrap_compat.sh bash test/integration_upgrade_cli.sh bash test/integration_css_minify.sh bash test/integration_new_plugins.sh npx playwright install chromium webkit npm run test:visual bundle exec al-folio upgrade audit bundle exec al-folio upgrade overrides audit bundle exec al-folio upgrade report docker compose up -d curl -fsS http://127.0.0.1:8080/al-folio/ >/dev/null docker compose logs --tail=80 docker compose down各阶段的作用可以拆解为四组:
- 依赖与静态检查:
bundle install安装 Ruby 依赖(注意 gem 版本由Gemfile.lock锁定);npm ci按锁文件安装前端依赖;npm run lint:prettier用 Prettier(配合@shopify/prettier-plugin-liquid,printWidth: 150)检查格式;npm run lint:style-contract执行上文分析的薄 Starter 边界契约检查。 - 构建与集成测试:
bundle exec jekyll build --baseurl /al-folio验证站点能完整构建;随后依次运行 7 个test/integration_*.sh脚本。以 test/integration_plugin_toggles.sh 为例,它用 Ruby/Psych 动态生成一份去掉指定插件的_config.yml覆盖文件,再以jekyll build --config "_config.yml,${override}"构建并断言index.html存在——分别对al_analytics、al_img_tools、al_search做开关验证。而 test/integration_upgrade_cli.sh 则在临时目录里构造一个最小站点,验证al-folio upgrade apply --safe会写入al_folio:契约键、upgrade audit --no-fail会生成带Non-blocking findings的al-folio-upgrade-report.md。 - 视觉回归:
npx playwright install chromium webkit安装浏览器内核后,npm run test:visual运行 test/visual/ 下的 Playwright 视觉一致性测试(distill 页面、交互行为、与线上站点的一致性比对)。 - 升级审计与 Docker 冒烟:
bundle exec al-folio upgrade audit/overrides audit/report检查 v1 配置契约与本地覆盖漂移;最后用docker compose up -d起容器、curl探活/al-folio/、查看日志并关闭。
关于测试门控:全部 7 个test/integration_*.sh脚本由.github/workflows/unit-tests.yml统一门控,你只需运行与本次改动相关的脚本即可。Docker 方面有两点值得注意:v1 使用/srv/jekyll/bin/entry_point.sh作为入口,站点输出到容器本地的/tmp/_site,以此避免宿主 bind-mount 写入死锁。
六、提交 PR 之前的检查清单
AGENTS.md 在命令集之后给出了提交 PR 前的四条硬性要求:
- Starter 的活留在 starter,运行时路由到所属插件仓库:这是贯穿全文的第一原则,再次被强调。
- 跑
npm run lint:prettier:Prettier 配合@shopify/prettier-plugin-liquid、printWidth: 150;格式问题可用npx prettier . --write自动修复。 - 保持文档与 v1 归属一致,且"每个事实只放一处":优先链接而非重复转述,防止文档漂移。
- 若创建或保留了插件拥有文件的本地覆盖:运行
bundle exec al-folio upgrade overrides audit,审阅后提交.al-folio-overrides.yml。
关于本地覆盖的运维流程,docs/ARCHITECTURE.md 给出完整命令:
bundle exec al-folio upgrade overrides audit bundle exec al-folio upgrade overrides diff <path> bundle exec al-folio upgrade overrides accept <path>overrides audit会把所属 gem、版本以及上游/本地 SHA256 记录进.al-folio-overrides.yml;后续bundle update改变上游文件时,审计会将该覆盖标记为 stale。值得惠及所有人的修复应移植回所属 gem,而不是一直作为本地覆盖保留。值得注意的是,这个覆盖流程适用于用户自己的站点;而在本 starter 仓库内,npm run lint:style-contract会直接禁止这些目录存在(见第三节)。
七、Agent 技能文件与进一步阅读
仓库在.agents/skills/下提供了两个开箱即用的 Agent 技能(SKILL.md),分别覆盖两类典型任务:
- .agents/skills/al-folio-bootstrap/SKILL.md:从模板创建、配置、个性化一个新 v1.x 站点的完整工作流。核心步骤是:先读
AGENTS.md与docs/BOUNDARIES.md;优先用_config.yml、_data、内容集合与站点资产做定制;不要复制插件拥有的 runtime 文件;只有配置与内容无法表达需求时才使用本地_includes/_layouts/_sass覆盖;交还前用npm ci、npm run lint:prettier、bundle exec al-folio upgrade audit --no-fail、bundle exec jekyll build --baseurl /al-folio验证。 - .agents/skills/al-folio-v1-migration/SKILL.md:把既有定制化 fork 迁移到 v1.x 的工作流。核心步骤是:在一次性分支/fork/clone 中操作、不覆盖原站点;从 v1 starter 契约出发,再迁入站点自有文件;保留
theme: al_folio_core与 bundled gem 接线;删除已被插件拥有的过期 runtime 拷贝(除非是有意覆盖);用al-folio upgrade audit/overrides audit/overrides diff/overrides accept处理覆盖漂移并提交.al-folio-overrides.yml。
另外,.codex/skills和.claude/skills是.agents/skills的符号链接,供不同 Agent 生态发现同一套技能。
最后,AGENTS.md 推荐的进一步阅读路径如下,按需深入:
- docs/ARCHITECTURE.md——Starter 与 gem 如何组合、静默失败模式、v1 配置契约、本地覆盖;
- docs/BOUNDARIES.md——权威的"区域 → gem"归属表与 PR 分诊手册;
- docs/CONTRIBUTING.md——贡献者工作流与 Agent 工具链;
- docs/README.md——全部用户与维护者指南的索引。
一个值得一提的细节:AGENTS.md 被列在 _config.yml 的exclude:清单里,因此它不会被打进最终构建的站点——它是一份面向协作者(尤其是 Coding Agent)的元文档,而不是面向站点访客的内容。理解了这一点,也就理解了它在整个仓库协作体系中的独特位置:它是薄 Starter 架构下"谁拥有什么、改动该去哪里、如何自证无错"的单一事实入口。
【免费下载链接】al-folioA beautiful, simple, clean, and responsive Jekyll theme for academics项目地址: https://gitcode.com/GitHub_Trending/al/al-folio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考