Skip to content

Emitted-artifact templates move out of Python source into package data; CLI command table becomes data - #99

Closed
open-coder-ai wants to merge 3 commits into
mainfrom
feat/w48-templates-to-data
Closed

Emitted-artifact templates move out of Python source into package data; CLI command table becomes data#99
open-coder-ai wants to merge 3 commits into
mainfrom
feat/w48-templates-to-data

Conversation

@open-coder-ai

Copy link
Copy Markdown
Owner

What

Every template for an emitted artifact written in a non-Python language moves out of Python string literals into template files under src/chock/data/templates/, loaded via importlib.resources (chock.resources.template_text / render_template / render_template_line). Placeholders are inert __TOKEN__ markers swapped by str.replace — never .format()/f-strings — so every template file is valid in its own language as-is and is linted as such. The CLI's COMMANDS table likewise moves to src/chock/data/commands.json, read by cli.py, which keeps its lazy-import dispatch. The compact externalized_text hard rule is recorded in AGENTS.md beside code_comments.

Moved (17 template files):

  • compile/emitters/ci.py STEP_TEMPLATE → ci/step.yaml (__POLICY_ID__, __GATE_PATH__)
  • compile/emitters/git_hook.py SHIM_TEMPLATE → git-hook/shim.sh (__POLICY_ID__, __EVENT__)
  • hooks/installers.py dispatcher + policy wrapper + Windows validate wrapper → hooks/*.sh (__EVENT__, __SOURCE__, __PS1_NAME__; the ownership marker is literal in the files and pinned to ownership.GENERATED_MARKER by test)
  • compile/emitters/in_agent.py shell + PowerShell one-liners → in-agent/guard-command.{sh,ps1} (__ADAPTER__, __GUARD__)
  • gate/runtime_bundle.py handler sources → runtime/*.py.tmpl (each valid Python as-is; the session-start dispatch is a second full variant rather than an unparseable indented fragment, with a test binding the two variants to differ only by the branch)
  • scaffold/install_ci.py workflow YAML → scaffold/ci-workflow.yml (complete workflow; MARKER now derives from the template's first line so the two cannot drift)
  • scaffold/templates.py three scaffolds, scaffold/agents_md.py pointer block, scaffold/skills_bridge.py bridge marker (survey delta beyond the dispatch inventory) → scaffold/*

Byte-identity proof: emitter goldens and runtime goldens unchanged; full-artifact before/after diff (chock sync; .chock .claude .cursor .github/hooks; chock plugin build --format all; chock marketplace build; gate-events.jsonl excluded) — 490 files, zero bytes differ, symlink targets included. chock --help frozen byte-for-byte by a new golden.

Guards added:

  • tests/test_template_data.py: every template is rendered by some renderer and vice versa (AST-derived); every __TOKEN__ in a file equals the tokens its renderer supplies (both directions); .py.tmpl parse as Python, .sh pass bash -n, YAML parses; rendered step fragment pinned (actionlint can't lint a fragment); ownership-marker and pointer-marker derivations.
  • New CI template-lint job: shellcheck over the shell templates (-s sh for the shebang-less one-liner), actionlint over the complete workflow template; tools hash-pinned via requirements/template-lint.txt (pip-compile --generate-hashes; shellcheck-py/actionlint-py bundle the binaries, keeping CI and local runs on the same pins).
  • Package-data: data/templates/**/* added to [tool.setuptools.package-data], pinned by a derivation test in tests/test_wheel_install.py (every on-disk template must be in the built wheel). commands.json rides the existing data/*.json pattern and is pinned by the existing evidence-ledger wheel test's directory iteration.
  • Frozen binary: the spec's existing collect_data_files("chock") collects the tree (.py.tmpl deliberately not .py, which collect_data_files skips); verified in-process against the spec's own collection call, plus a simulated _MEIPASS load; the CI binary smoke now greps the rendered pointer block out of AGENTS.md so a frozen template read is proven on every build. (A local PyInstaller build was attempted and failed in the container on an unrelated broken system cryptography while PyInstaller's hook enumerated it — environmental; the CI binary job is the gate.)
  • tests/test_cli_commands.py: every module path in commands.json imports and exposes its entrypoint; each entry carries exactly one of help/alias_of; --help matches the golden.

Out of scope by design: error/diagnostic message strings (they stay beside the condition that raises them — e.g. gate/sessionstart.py's instruction text, authoring/matrix.py's usage text) and plugin posture prose (derived by C1/C3). Pre-existing chock-init packaged templates still use {{repo_name}}/.format() placeholders; converting them is follow-up, not this move.

Definition of done

  • chock check → 0 errors, 0 warnings, 0 infos
  • chock check --only matrix passes; matrix updated in this PR if behavior changed (no behavior change)
  • chock sync --repo . --check clean (compiled artifacts match their manifests)
  • chock check --only verify clean (lockfile matches packs and compiled artifacts)
  • Registry rescanned; no stale entries
  • pytest -q green (1047 passed); new checks have attack + ordinary-data tests (token mismatch, orphan template, marker drift all assert; ordinary renders pinned)
  • pytest acceptance/ -c acceptance/pytest.ini --rootdir=acceptance green (21 passed; packaging changed)
  • Existing artifacts migrated in this PR if a check was added/extended (none needed — output bytes unchanged)
  • Touched manifests: version bump + changelog entry (no manifest touched; CHANGELOG Unreleased entry added; no version bump — patch-level byte-identical output)
  • ruff check . and ruff format --check . clean

Claims

  • No surface is described as enforcing more than it installs. Nothing emitted or installed changed — INSTALLED_SURFACES, the coverage table, and docs/enforcement-surfaces.md are untouched and still agree.

🤖 Generated with Claude Code


Generated by Claude Code

Every template for an emitted artifact written in another language (the CI
gate step YAML, the git-hook shim, the hook installer's dispatcher and
wrappers, the in-agent guard one-liners, the scaffold workflow/gitattributes/
guardrails/pointer block, the skills-bridge marker, and the runtime-bundle
handler sources as .py.tmpl) now lives under src/chock/data/templates/,
loaded via importlib.resources. Placeholders are inert __TOKEN__ markers
swapped by str.replace, so each file is valid in its own language as-is: a
new template-lint CI job shellchecks the shell templates and actionlints the
complete workflow template (tools hash-pinned in requirements/template-lint.txt),
and tests/test_template_data.py asserts every template is rendered, every
token round-trips between file and renderer, Python templates parse, and the
ownership markers survive. The template tree is covered by package-data
(pinned in tests/test_wheel_install.py) and by the PyInstaller spec's
existing collect_data_files("chock"); the binary smoke now greps the
rendered pointer block to prove a frozen template read.

Emitted bytes are unchanged: emitter goldens, runtime goldens, and a full
before/after artifact diff (sync + plugin build + marketplace build, 490
files) are byte-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
The COMMANDS table in cli.py was data: command name, module path, optional
entry function, help or alias text. It now lives in
src/chock/data/commands.json (group -> name -> {module, fn?, help|alias_of}),
read at startup by cli.py, which builds the same lazy-import dispatch from it.
tests/test_cli_commands.py asserts every module in the table imports and
exposes its entrypoint, that each entry carries exactly one of help/alias_of,
and that chock --help matches the new byte-for-byte golden
(tests/fixtures/cli_help.txt); the help output is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Adds the compact externalized_text hard rule beside code_comments: 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 and
behaviour stay in code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
@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.

optimize, policy-init, validate) are refreshed only by `chock install-skills .`, which
preserves local edits.
"""
_GITATTRIBUTES_TEMPLATE = template_text("scaffold/gitattributes")
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