diff --git a/.github/workflows/jekyll.yml b/.github/workflows/jekyll.yml index 5800afa..0cb1449 100644 --- a/.github/workflows/jekyll.yml +++ b/.github/workflows/jekyll.yml @@ -67,9 +67,10 @@ jobs: id: pages uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - name: Build with Jekyll - run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" --strict-front-matter + run: bundle exec jekyll build --baseurl "$BASE_PATH" --strict-front-matter env: JEKYLL_ENV: production + BASE_PATH: ${{ steps.pages.outputs.base_path }} - name: Validate HTML, internal links, and images # Catches broken internal links/anchors and missing images before deploy. # External links are skipped (slow, flaky) but must be https; hash-only @@ -80,7 +81,9 @@ jobs: bundle exec htmlproofer ./_site \ --disable-external \ --allow-hash-href \ - --swap-urls "^${{ steps.pages.outputs.base_path }}:" + --swap-urls "^$BASE_PATH:" + env: + BASE_PATH: ${{ steps.pages.outputs.base_path }} - name: Validate discoverability (sitemap, feed, metadata) # Guards what htmlproofer does not look at: that the sitemap and feed # parse, that every page carries a description and a canonical, that no diff --git a/README.ko.md b/README.ko.md index 8a96de4..16c5d23 100644 --- a/README.ko.md +++ b/README.ko.md @@ -88,7 +88,7 @@ js/ main.js(테마 토글·코드 복사·목차·메뉴·이미 assets/images/ 토픽별 커버 이미지와 글의 그림·다이어그램(플랫) assets// 일부 오래된 글의 그림 폴더. 새 글은 assets/images/를 씀 search.json 전체 본문 검색 색인 (simple-jekyll-search가 사용) -test/ _plugins/ 로직의 minitest 단위 테스트 +test/ _plugins/와 글 린트의 minitest 단위 테스트 script/ lint-posts.rb (빌드 전 글 원본 규칙 검사) validate-site.sh (빌드 후 검색 노출 검사) check-plugin-output.rb (플러그인 훅이 글에 남긴 결과 검사, @@ -108,8 +108,8 @@ _data/ post_conventions.yml (카테고리·표지·통제 태그· ## 글 작성하기 -가장 쉬운 방법은 `/write-post` 스킬입니다. 리서치 → 초안 → 퇴고까지 한 번에 진행해 줍니다. -직접 만들 때는 `_posts/YYYY-MM-DD-slug.md` 파일을 만들고 아래 프런트매터로 시작하세요. +논문 리뷰는 scholar-lens가 PR로 올리고, 나머지 글은 `/write-post` 스킬(로컬 전용, +gitignore된 `.claude/` 아래)이 리서치 → 초안 → 퇴고를 진행합니다. 직접 만들 때는 `_posts/YYYY-MM-DD-slug.md` 파일을 만들고 아래 프런트매터로 시작하세요. ```yaml --- @@ -117,13 +117,13 @@ layout: post title: "<글 제목>" subtitle: "<한 줄 소개>" # 선택 — 헤더의 제목 아래에 표시됩니다 date: YYYY-MM-DD HH:MM:SS -paper_author: "<기관>" # 논문을 낸 기관(사람 이름 아님). Insights·의견 글이면 생략 +paper_author: "<기관>" # 논문을 낸 기관(사람 이름 아님). 논문 글에만 seo_title: "<짧은 제목>" # 선택 — 논문 제목이 너무 길 때 에 대신 씁니다 description: >- # 선택, 평문 — 아래 설명 참고 <검색 결과에 노출될 문장, 150자 안팎> categories: ["<유형>", "<주제>"] tags: ["<태그-1>", "<태그-2>"] -cover: /assets/images/<topic>.(jpg|png) # Insights는 insights.jpg +cover: /assets/images/<topic>.(jpg|png) # 유형이나 주제의 이미지, 아래 참고 use_math: true # 수식이 있을 때만 (MathJax를 불러옵니다) lang: ko # translation_id를 쓰면 필수(ko | en)… translation_id: <공통-슬러그> # …한국어 글과 -en 번역본을 연결합니다. @@ -146,17 +146,10 @@ RSS `<summary>`에 실리는 문장입니다. 생략하면 `_plugins/post_descri 첫 산문 단락에서 뽑아 주는데, 대개 그걸로 충분합니다. 다만 첫 단락이 인용문이나 공개 고지로 시작하는 글 — 즉 대부분의 Insights 글 — 은 직접 쓰는 편이 낫습니다. -설명문은 **사이트 전체에서 중복되면 안 됩니다.** 두 페이지가 같은 설명문을 쓰면 CI가 +설명문은 **사이트 전체에서 중복되면 안 되고** 페이지 제목보다 길어야 합니다. 어기면 CI가 빌드를 실패시킵니다. 설명문은 평문으로 나가므로 `$$`, `*강조*`, 백틱, 링크, 태그를 쓰면 린트가 막습니다. -### 날짜, 파일명, Liquid - -파일명은 게시일(`YYYY-MM-DD-`)로 시작합니다. `date:`는 그보다 이를 수(논문 발행일)는 있어도 -늦을 수는 없습니다. Jekyll은 모든 글을 먼저 Liquid로 렌더하므로, 코드 블록의 `{{ … }}`나 -`{% … %}`(프롬프트 템플릿, GitHub Actions의 `${{ … }}`)는 빈칸이 되거나 빌드를 멈춥니다. -`{% raw %}…{% endraw %}`로 감쌉니다. 세 가지 모두 린트가 확인합니다. - ### 카테고리가 URL을 결정합니다 카테고리는 **두 단계**입니다. @@ -166,8 +159,9 @@ RSS `<summary>`에 실리는 문장입니다. 생략하면 `_plugins/post_descri **탭을 유지하고** 빈 상태 문구를 띄웁니다 — 탭이 없으면 채우는 중인 섹션이 아니라 없어진 섹션으로 읽히기 때문입니다. - `categories[1]` — **주제**: `_data/post_conventions.yml`의 `topics` 중 하나이며, 표지 - 이미지도 거기서 정해집니다(Insights 글은 유형의 `insights.jpg`를 씁니다). 맞는 주제가 없으면 먼저 거기에 추가합니다. 목록에 없는 값은 - 린트가 막습니다. + 이미지도 거기서 정해집니다. Insights 글은 유형의 `insights.jpg`를 쓰고, 이미지가 없는 + 주제는 저장소에 있는 아무 이미지나 씁니다. 맞는 주제가 없으면 먼저 거기에 추가합니다. + 목록에 없는 값은 린트가 막습니다. Jekyll은 이 둘을 슬러그로 바꾸고(`_config.yml`의 `permalink`) 날짜와 합쳐 출력 경로를 만듭니다. @@ -216,26 +210,23 @@ _site/paper-reviews/language-models/2025/01/23/<slug>.html kramdown이 내용을 그대로 보존해 `\(…\)`로 내보내므로 안전합니다. (`$10M` 같은 본문 속 달러 표기는 수식이 아니니 그대로 둬도 됩니다.) -린트는 렌더링이 깨지는 다음 경우도 막습니다. - -- `\\(…\\)`·`\\[…\\]`로 쓴 수식, 단일 `$` 옆의 TeX: 위와 같이 마크다운이 수식을 먼저 - 읽습니다. -- `$$…$$` 안의 `\_`: MathJax가 아래첨자가 아닌 밑줄 문자를 그대로 그립니다. `_`로 쓰고, - `\text{…}` 안에서만 이스케이프합니다. -- 앞뒤에 빈 줄이 없는 디스플레이 `$$…$$` 줄(목록 안 포함): kramdown이 문단에 붙여 인라인 - 수식으로 처리합니다. -- `$$` 구간 안의 한글: 거의 언제나 통화 표기 `$$` 두 개가 한 구간으로 읽힌 것입니다. -- `use_math: true` 없이 쓴 `$$` 수식: MathJax가 로드되지 않아 TeX가 글자로 보입니다. -- HTML이 아닌 `<이름>`, `<s>`·`<input>`, 또는 속성 없는 `<img>`(모델의 특수 토큰): kramdown이 그대로 - 넘기고 브라우저가 지웁니다. `코드`로 씁니다. -- 마커 뒤에 언어 말고 다른 것이 붙은 코드 펜스(```` ```python title="a" ````)나 닫히지 않은 - 펜스: kramdown이 펜스로 읽지 않습니다. -- 글마다 대소문자를 다르게 쓴 태그(`Grouped-Query-…`와 `Grouped-query-…`). -- 표 행의 `$$` 안에 있는 `|`: kramdown이 거기서 셀을 나누고, `\|`는 `|`로 바꿉니다(노름이 - 절댓값이 됩니다). `\lVert … \rVert`를 씁니다. - -인라인 수식에서는 맨 `|` 대신 `\lvert … \rvert`(또는 `\mid`)를, 노름에는 `\lVert … \rVert`를 씁니다. 린트는 표 안에서만 -잡습니다. +수식 안의 막대는 `\lvert … \rvert`나 `\mid`로, 노름은 `\lVert … \rVert`로 쓰고 맨 `|`는 +쓰지 않습니다. 표 행에서는 kramdown이 거기서 셀을 나눕니다. + +### 린트가 잡는 그 밖의 것 + +`ruby script/lint-posts.rb`는 찾은 줄과 고치는 방법을 함께 알려 줍니다. 자주 걸리는 +규칙은 다음과 같습니다. 모두 다른 검사는 통과하는데 페이지가 잘못 렌더링되는 경우입니다. + +- 본문의 `{{ … }}`·`{% … %}`(프롬프트 템플릿, GitHub Actions의 `${{ … }}`): Jekyll이 글을 + 먼저 Liquid로 렌더링합니다. `{% raw %}…{% endraw %}`로 감쌉니다. +- 코드 밖의 `<think>`·`<s>` 같은 모델 토큰: 브라우저가 지웁니다. `코드`로 씁니다. +- `use_math: true` 없는 `$$` 수식, `\\(…\\)`로 쓴 수식, `$$` 안의 `\_`, 앞뒤 빈 줄이 없는 + 디스플레이 `$$` 줄. +- 마커 뒤에 언어 말고 다른 것이 붙은 코드 펜스, 닫히지 않은 펜스. +- 게시일로 시작하지 않는 파일명, 그보다 늦은 `date:`. +- ar5iv 링크: ar5iv는 잃어버린 그림 대신 대체 이미지를 HTTP 200으로 돌려줍니다. + `arxiv.org/html/<id>v1/…`를 쓰거나 그림을 `assets/images/`에 복사합니다. ### 푸시 전에 확인하세요 diff --git a/README.md b/README.md index 44a739c..bccc3bf 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ js/ main.js (theme toggle, code-copy, TOC, menu, image zoom…) assets/images/ Topic cover images, plus post figures and diagrams (flat) assets/<slug>/ Figures of some older posts; new posts use assets/images/ search.json Full-text search index (consumed by simple-jekyll-search) -test/ minitest unit tests for the _plugins/ logic +test/ minitest unit tests for _plugins/ and the post lint script/ lint-posts.rb (post sources vs the conventions, before the build) validate-site.sh (post-build discoverability checks) check-plugin-output.rb (each plugin hook's mark on the posts; @@ -111,8 +111,9 @@ no source file for any of them. ## Writing a post -The easiest path is the `/write-post` skill, which runs the whole -research → draft → proofread workflow. To add one by hand, create +Paper reviews come from scholar-lens as pull requests; the `/write-post` skill +(local, under the gitignored `.claude/`) runs research → draft → proofread for +the rest. To add one by hand, create `_posts/YYYY-MM-DD-slug.md` starting with this front matter: ```yaml @@ -121,13 +122,13 @@ layout: post title: "<Post Title>" subtitle: "<one-line pitch>" # optional — shown under the title in the header date: YYYY-MM-DD HH:MM:SS -paper_author: "<Org>" # the paper's org, not people; omit for Insights/opinion posts +paper_author: "<Org>" # the paper's org, not people; paper posts only seo_title: "<shorter title>" # optional — for <title> when the paper title is too long description: >- # optional, plain text — see below <search-snippet, ~150 chars> categories: ["<Type>", "<Topic>"] tags: ["<Tag-1>", "<Tag-2>"] -cover: /assets/images/<topic>.(jpg|png) # Insights: insights.jpg +cover: /assets/images/<topic>.(jpg|png) # the type's or topic's image; see below use_math: true # ONLY if the post has equations (loads MathJax) lang: ko # required with translation_id (ko | en)… translation_id: <shared-slug> # …links a Korean post to its -en twin; @@ -151,17 +152,9 @@ derives one from the post's first real prose paragraph, which is usually good enough. Write it by hand when the first paragraph opens on a pull quote or a disclosure note — that is, on most Insights posts. -Descriptions must be **unique across the site**; CI fails the build if two pages -share one. They ship as plain text, so the lint rejects `$$`, `*emphasis*`, -backticks, links and tags in them. - -### Dates, file names and Liquid - -The file name starts with the posting day (`YYYY-MM-DD-`); `date:` may be earlier -(a paper's date) but not later. Jekyll renders every post as Liquid first, so a -`{{ … }}` or `{% … %}` in a code block (a prompt template, a GitHub Actions -`${{ … }}`) renders as nothing, or stops the build: wrap it in `{% raw %}…{% endraw %}`. The lint -checks all three. +Descriptions must be **unique across the site** and longer than the page title; +CI fails the build otherwise. They ship as plain text, so the lint rejects +`$$`, `*emphasis*`, backticks, links and tags in them. ### Categories drive the URL @@ -172,9 +165,10 @@ Categories are **two levels**: A type with no posts yet keeps its tab and renders an empty-state line: a missing tab reads as a section that was removed, not one still filling up. - `categories[1]` — the **topic**, one of the `topics` in - `_data/post_conventions.yml`, which also gives its cover image if it has one (Insights - posts use the type's `insights.jpg` instead). When none fits, - add one there first; the lint rejects any other value. + `_data/post_conventions.yml`, which also gives its cover image if it has one. + Insights posts use the type's `insights.jpg`; a topic without an image takes any + existing one. When no topic fits, add one there first; the lint rejects any + other value. Jekyll slugifies the two and combines them with the date to build the output path (`permalink` in `_config.yml`): @@ -226,28 +220,25 @@ MathJax runs — e.g. `$a*b*c$` becomes `$a<em>b</em>c$` and renders broken. Wit `$$`, kramdown emits verbatim `\(…\)` and leaves the contents alone. (Prose dollar signs like `$10M` are fine — they're not math.) -The lint also rejects what renders wrong: - -- math written `\\(…\\)` or `\\[…\\]`, or TeX next to a single `$`: Markdown - reads the formula first, as above. -- `\_` inside `$$…$$`: MathJax renders a literal underscore, not a subscript. Write - `_`; escape it only inside `\text{…}`. -- a display `$$…$$` line without a blank line before and after, in lists too: - kramdown folds it into the paragraph as inline math. -- Hangul inside a `$$` span: almost always two currency `$$`s read as one span. -- `$$` math in a post without `use_math: true`: MathJax never loads, so the TeX - shows as text. -- a `<name>` that is not HTML, `<s>`/`<input>`, or a bare `<img>` (a model's special token): - kramdown passes it through and the browser drops it. Write it in `code`. -- a code fence with more than a language after the marker (```` ```python title="a" ````), - or one never closed: kramdown does not read it as a fence. -- a tag spelled two ways across posts (`Grouped-Query-…` and `Grouped-query-…`). -- a `|` inside `$$` in a table row: kramdown splits the cell there, and turns - `\|` into `|` (a norm into an absolute value). Write `\lVert … \rVert`. - -Inside inline math, write `\lvert … \rvert` (or `\mid`) instead of a bare `|`, and -`\lVert … \rVert` for a norm; -the lint catches it only in tables. +Inside math, write `\lvert … \rvert` or `\mid` for a bar and `\lVert … \rVert` +for a norm, never a bare `|`: in a table row kramdown splits the cell there. + +### What else the lint catches + +`ruby script/lint-posts.rb` names the line and the fix for each finding. The +rules easiest to trip, each a page that renders wrong with every other gate green: + +- `{{ … }}` or `{% … %}` in the body (a prompt template, GitHub Actions' + `${{ … }}`): Jekyll renders posts as Liquid first. Wrap it in + `{% raw %}…{% endraw %}`. +- a model token such as `<think>` or `<s>` outside code: the browser drops it. + Write it in `code`. +- `$$` math without `use_math: true`, math written `\\(…\\)`, `\_` inside `$$`, + a display `$$` line without blank lines around it. +- a code fence with more than a language after the marker, or one never closed. +- a file name that does not start with the posting day, or a `date:` after it. +- an ar5iv link: ar5iv now answers a lost figure with a placeholder image and + HTTP 200. Use `arxiv.org/html/<id>v1/…`, or copy the figure into `assets/images/`. ### Validate before pushing diff --git a/_config.yml b/_config.yml index 1212fde..e7e50e5 100644 --- a/_config.yml +++ b/_config.yml @@ -32,10 +32,8 @@ exclude: title: Bits, Bytes and Neural Networks subtitle: A Data Scientist's Digital Playground email: aldente0630@gmail.com -name: Jonas Kim -# jekyll-feed reads `author` (not `name`) for the Atom <author> element. Without -# it every entry ships an empty <author><name></name></author>. Posts never set -# `author` themselves — paper posts use `paper_author` — so this is every entry's. +# The site's author: jekyll-feed's Atom <author> and head.html's author meta and +# JSON-LD. Posts never set `author` themselves — paper posts use `paper_author`. author: Jonas Kim # The site's default language: the feed's xml:lang. Pages and posts still set # their own `lang`; a post without one is in this language. @@ -55,7 +53,7 @@ url: https://bits-bytes-nn.github.io # afternoon-dated posts onto the next day and 404 their live URLs. Pinned to UTC # because every published URL was built that way. timezone: UTC -# Build posts dated later than the build. Posts here are dated 12:00 on their +# Build posts dated later than the build. Insights posts are dated 12:00 on their # day (21:00 KST), so a same-day merge in the Korean morning would deploy # without the post, and nothing rebuilds the site later. Nothing is scheduled. future: true diff --git a/_includes/head.html b/_includes/head.html index 78a929a..3b8968c 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -85,9 +85,9 @@ <title>{{ html_title }} - {% if site.name %} - - + {% if site.author %} + + {% endif %} - {% if site.protocols.fb_image %} @@ -133,7 +132,6 @@ {% endif %} {% for cat in page.categories %} {% endfor %}{% for tag in page.tags %} {% endfor %}{% endif %} - {% endif %}