Skip to content

Externalize emitted-artifact templates and the CLI command table into data files - #100

Merged
open-coder-ai merged 3 commits into
mainfrom
w48/emitter-templates-to-data
Sep 2, 2026
Merged

Externalize emitted-artifact templates and the CLI command table into data files#100
open-coder-ai merged 3 commits into
mainfrom
w48/emitter-templates-to-data

Conversation

@open-coder-ai

@open-coder-ai open-coder-ai commented Sep 2, 2026

Copy link
Copy Markdown
Owner

What

Applies the owner's "externalize, don't hardcode" standard (plan/coding-standards.md §2) to
every emitted-artifact template that was previously a Python string literal. Three fronts, one
PR (per brief plan/spine-a/briefs/w48-chock-templates-to-data.md):

  1. Templates out of Python source, into a single src/chock/data/templates/ tree (the
    brief's suggested path), read via chock.resources.template_text/render_template and
    rendered with __TOKEN__ + str.replace (never .format()), so each template file is valid
    in its own language as committed:

    • compile/emitters/ci.py (CI-gate step YAML), git_hook.py (git-hook shim, bash)
    • compile/emitters/in_agent.py (bash + PowerShell one-liners)
    • hooks/installers.py (dispatcher + two wrapper scripts, sh)
    • scaffold/install_ci.py (scaffolded CI workflow YAML), templates.py (three config-file
      scaffolds), agents_md.py (pointer block)
    • scaffold/skills_bridge.py (the bridge-directory marker body)
    • gate/runtime_bundle.py (vendored-runtime Python-source fragments, as .py.tmpl)

    Out of scope by owner decision: error/diagnostic message strings (stay beside the condition
    that raises them) and plugin posture prose (plugin/posture.py and per-store descriptions —
    C1/C3 derive those).

  2. AGENTS.md gains a compact externalized_text hard rule recording the standard, beside
    code_comments.

  3. cli.py's COMMANDS table becomes data. The name → (module, help/alias) table moves to
    data/commands.json (grouped, order preserved), read once at import time; lazy-import
    (_module_main/_module_fn) behaviour is unchanged.

Proof of byte-identical output

  • tests/test_template_data.py: every template renders, every __TOKEN__ round-trips between
    file and renderer (no orphan, no missing), Python .tmpl fragments parse, ownership markers
    survive.
  • tests/test_emitter_stability.py (existing goldens) and the runtime-bundle goldens pass
    unchanged.
  • Full C2-recipe artifact diff, run independently as part of reconciling this PR: chock sync, then .chock/, .claude/, .cursor/, .github/hooks/ compared byte-for-byte between
    this repo's pre-change state (25d9e96) and this branch, plus chock plugin build --format all and chock marketplace build (all three vendor trees) compared the same way — zero
    bytes differ
    anywhere (gate-events.jsonl excluded, as specified).
  • chock --help output is byte-identical, golden-pinned (tests/fixtures/cli_help.txt); every
    module path in commands.json imports and exposes its entry point
    (tests/test_cli_commands.py).

Package-data / frozen-binary coverage

  • pyproject.toml: "chock" = ["data/*.json", "data/templates/**/*"].
  • tests/test_wheel_install.py: the built wheel ships every file under data/templates/
    (verified directly against a real wheel build).
  • packaging/chock.spec's existing collect_data_files("chock") needed no changes — verified
    directly (not assumed) that it already reaches every file under data/templates/; the CI
    binary smoke greps the rendered pointer block to prove a frozen-binary template read.

CI linting of templates

New template-lint job: shellcheck (via the shellcheck-py PyPI wheel, hash-pinned like every
other tool in this repo) on the shell templates, actionlint (via actionlint-py) on the
complete scaffolded CI workflow template — not the ci-gate step fragment, since actionlint needs
a complete workflow; the fragment is unit-tested instead.

Note on how this PR came together

This PR's first three commits continue a predecessor session's branch
(feat/w48-templates-to-data, corrected into my spawn via an orchestrator amendment after I'd
already built an independent, differently-structured implementation of the same brief). I
verified the predecessor's work critically rather than trusting it — reran the full test suite,
chock check, chock sync --check, ruff, the acceptance suite, a real wheel build, a real
PyInstaller build/collection check, and the full C2 artifact diff myself from scratch — before
building on it. The one issue I found and fixed on top (last commit): CodeQL flagged
_GITATTRIBUTES_TEMPLATE as an unused global (its leading underscore reads as module-private,
but it's genuinely consumed from scaffold/init.py); renamed to match its two siblings in the
same file, which already use no underscore for the identical cross-module usage.

Definition of done

  • chock check → 0 errors, 0 warnings, 0 infos
  • chock check --only matrix passes (no behavior change)
  • chock sync --repo . --check clean
  • chock check --only verify clean
  • Registry rescanned; no stale entries
  • pytest -q green (1047 passed, 1 skipped, pre-existing)
  • pytest acceptance/ -c acceptance/pytest.ini --rootdir=acceptance green (packaging touched)
  • No version bump (byte-identical emitted output; brief explicitly asks for no version bump)
  • CHANGELOG Unreleased entry added
  • ruff check . and ruff format --check . clean

Claims

  • No surface is described as enforcing more than it installs — this PR changes how
    templates are stored, not what is emitted or installed; INSTALLED_SURFACES and the
    coverage table are unaffected.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

preserves local edits.
"""
_SCAFFOLD_DATA_DIR = package_data_dir("chock.scaffold", "data")
_GITATTRIBUTES_TEMPLATE = _SCAFFOLD_DATA_DIR.joinpath("gitattributes.txt").read_text(encoding="utf-8")
Every emitted-artifact template written in a language other than Python
(YAML, shell, PowerShell, and the vendored-runtime Python-source fragments)
moves out of Python string literals into template files under each
package's own data/ directory, loaded via importlib.resources and rendered
with __TOKEN__ + str.replace instead of .format() -- so each template file
is valid in its own language as committed and CI lints it that way
(shellcheck, actionlint).

Covers: compile/emitters/{ci,git_hook,in_agent}.py, hooks/installers.py,
scaffold/{install_ci,templates,agents_md}.py, gate/runtime_bundle.py.
Emitted bytes are unchanged, proven by the existing emitter-stability
goldens, this repo's own `chock sync --check`, and new token round-trip
tests. New package-data and PyInstaller coverage tests guard every new
data/ directory.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Compact hard-rule entry beside code_comments, mirroring the owner
standard (plan/coding-standards.md, externalize-dont-hardcode): non-Python
text and vendor facts live in data/template files read by code, token
placeholders swapped by str.replace, template files linted as their own
language, package-data and frozen-binary coverage tested, error messages
stay beside their raising condition, behaviour stays code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
The COMMANDS table (name -> module path, help/alias text) is a table of
facts, not code: it moves to data/commands.json (grouped everyday/
authoring/aliases, preserving order), read once at import time to build
EVERYDAY/AUTHORING/ALIASES/COMMANDS. Lazy-import (_module_main/_module_fn)
behaviour is unchanged. `chock --help` output is byte-identical (golden
in tests/fixtures/cli_help_golden.txt); a new test asserts every module
path in the table imports and exposes its entry point.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
@open-coder-ai
open-coder-ai force-pushed the w48/emitter-templates-to-data branch from dcb59f2 to 71c89ea Compare September 2, 2026 02:13
@open-coder-ai
open-coder-ai marked this pull request as ready for review September 2, 2026 02:24
@open-coder-ai
open-coder-ai merged commit 683386e into main Sep 2, 2026
15 of 16 checks passed

Copy link
Copy Markdown
Owner Author

The CodeQL finding on _GITATTRIBUTES_TEMPLATE (unused global) above, plus one more emitted-artifact template this wave's inventory missed (scaffold/skills_bridge.py's bridge-marker text), are fixed in a small follow-up: #101.


Generated by Claude Code

open-coder-ai added a commit that referenced this pull request Sep 2, 2026
…-rename-and-bridge-marker

Follow-up to #100: fix CodeQL finding, externalize the bridge marker
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants