diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index d960e55..cabd7ae 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ { "name": "throughline", "source": "./", - "description": "Continuous, state-aware session memory for Claude Code — capture what you did and what is, hand it off with judgment.", + "description": "Continuous, state-aware session memory for Claude Code - capture what you did and what is, hand it off with judgment.", "author": { "name": "Dynamic Agency", "email": "support@dynamicagency.com" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 157e36d..0e64fe2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,39 @@ jobs: echo "version drift: .claude-plugin/plugin.json=$c .codex-plugin/plugin.json=$x .opencode-plugin/package.json=$o" >&2 exit 1 fi + - name: Typography (Linux) + if: runner.os == 'Linux' + # Mechanizes Jason's writing-style rule (no em/en-dashes or curly + # quotes in outward-facing content) so it can't regress silently - + # scoped to files a reader/user actually opens (README, promo site, + # per-harness docs, the plugin marketplace description), not the + # whole tree: internal docs (CHANGELOG, HANDOFF, this project's own + # memory files, code comments) are explicitly exempt per the style + # guide. The pattern is written as escape sequences on purpose - a + # literal em-dash pasted into this step would make it match itself. + # + # LC_ALL pinned to a UTF-8 locale: git grep -P only enables PCRE2's + # UTF mode under a UTF-8 locale codeset, and \x{2013} exceeds PCRE2's + # 8-bit max outside it - under e.g. LC_ALL=C the pattern fails to + # COMPILE (rc=128), and a bare `if git grep ...; then FAIL; fi` reads + # that failure as "no match found" and passes clean. A gate whose + # whole point is "can't regress silently" must not itself have a + # silent-pass failure mode, so the exit code is checked explicitly + # instead of trusting the if/then branch alone. + run: | + set +e + hits=$(LC_ALL=C.UTF-8 git grep -I -n -P '[\x{2013}\x{2014}\x{2018}\x{2019}\x{201C}\x{201D}]' -- \ + README.md docs/*.md docs/index.html .claude-plugin/marketplace.json) + rc=$? + set -e + if [ "$rc" -eq 0 ]; then + printf '%s\n' "$hits" + echo "non-ASCII em/en-dash or curly quote in outward-facing files (see above) - use ASCII punctuation instead ( - , straight quotes)" >&2 + exit 1 + elif [ "$rc" -ne 1 ]; then + echo "typography check failed to run (git grep rc=$rc) - this must not read as a clean pass" >&2 + exit 1 + fi - name: Run hook tests run: sh tests/run.sh diff --git a/.local-ci.json b/.local-ci.json index 2e38837..a03f5bd 100644 --- a/.local-ci.json +++ b/.local-ci.json @@ -12,6 +12,10 @@ "label": "hook tests", "run": "sh tests/run.sh" }, + { + "label": "typography", + "run": "export LC_ALL=C.UTF-8; out=$(git grep -I -n -P '[—–‘’“”]' -- README.md docs/*.md docs/index.html .claude-plugin/marketplace.json); rc=$?; if [ \"$rc\" -eq 0 ]; then echo \"$out\" >&2; exit 1; elif [ \"$rc\" -ne 1 ]; then echo \"typography check failed to run (git grep rc=$rc)\" >&2; exit 1; fi" + }, { "label": "opencode plugin", "run": "cd .opencode-plugin && npm ci && npm run typecheck && npm test" diff --git a/CHANGELOG.md b/CHANGELOG.md index 36c30a8..8a1c9cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ All notable changes to throughline are documented here. Format loosely follows availability, live/archived buffer counts, and the three env vars' current values - so diagnosing "why isn't capture firing" no longer means reading `_lib.sh` and reasoning through the precedence rules by hand. +- **CI typography gate** (issue #72): a `git grep` step fails the build on + em/en-dashes or curly quotes in outward-facing files (`README.md`, + `docs/*.md`, `docs/index.html`, the plugin marketplace description) - + mechanizes the existing no-em-dash writing-style rule instead of relying + on review to catch a regression. Mirrored in `.local-ci.json`. Fixed one + live violation in `.claude-plugin/marketplace.json`'s description. ## [0.15.0]