Skip to main content

release

MUST READ before preparing a release commit or GitHub release -- the blocking GitHub preflight (dev/release_preflight.py), project.save() versioning, changelog, README, template sync verification, fresh-install smoke, and the post-push GitHub release procedure (references/github-release.md).

来源信息

仓库
dylanroscover/Embody
最近来源活动
2026年9月26日 06:32
检测到的 SKILL.md 语言
英语
星标
182
分支
11

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
2 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
release
description
MUST READ before preparing a release commit or GitHub release -- the blocking GitHub preflight (dev/release_preflight.py), project.save() versioning, changelog, README, template sync verification, fresh-install smoke, and the post-push GitHub release procedure (references/github-release.md).
# Release Commit Procedure When the user asks to prepare a release commit (e.g., "prep a commit for v217"), follow these steps in order. After a successful push, follow `references/github-release.md` for the GitHub release. ## Preflight: nothing outstanding on GitHub (blocking, before step 0) Run this from the release checkout (the repo root, on `dev`) before anything else, and again right before the push (`python3` on macOS): ``` python dev/release_preflight.py ``` - **BLOCKING:** open Dependabot, code-scanning or secret-scanning alerts; draft or triage security advisories (private vulnerability reports); open Dependabot PRs; a red latest push-CI run of any workflow on `main` or `dev`; a failed third-party check on either tip; real commits on `main` that the release checkout lacks (a hotfix never merged back: merge `origin/main` in and push). A check it cannot run also blocks, so a failed API call never reads as "nothing open". - **WARNING:** other open PRs, every open issue (tagged NEW since the last release), merged branches left on the remote. - **Exit 1 = STOP.** Show the user every item. Each blocker gets resolved (a Dependabot PR merged only after its platform e2e run is green, or closed; alerts fixed or dismissed with a reason; hotfixes back-merged) or the user explicitly accepts it. Only then re-run with `--ack "<key>"`, and only for keys the user named. Never ack on your own judgment. - **Report the warnings in the same message.** List any acked items in the release commit body so the decision is on record. Why: on 2026-09-11 embody.tools sign-in was down ~23h after a dependency bump (PR #105) and specimen submit had been broken 4 days by a rename; two hotfixes then sat on `main` without `dev`. The preflight keeps outstanding GitHub state from being skipped; the platform e2e gate is what catches a breaking bump. ## 0. Save the Project The entire save call is `project.save()` -- no arguments. TD increments the `.toe` filename's trailing build, the `onProjectPreSave` hook in `dev/embody/execute_src_ctrl.py` bumps `par.Version`, deletes the prior release `.tox`, and exports the new one. Filename and `par.Version` stay in lock-step. The release export honors `pre_release`/`post_release` hook DATs placed directly under the Embody COMP (none exist today; they fire on EVERY `project.save`, always in LIVE mode -- the Embody comp is never copy-staged, so such hooks would mutate the live comp). On any export failure the manifest is NOT written and the prior version's stale manifest is removed: a pre_release abort or save failure leaves NO release `.tox` for the new version, while a post_release failure leaves the fresh `.tox` WITHOUT a manifest -- check the log and `release/` before pushing. The self-updater's rollback backup export passes `run_hooks=False`, so shipped hooks never fire in user projects during updates. Don't pass a path (TD increments from *your* path's build, desyncing by one). Don't pre-set `par.Version`. Don't call `ExportPortableTox` directly. **If externalized files changed on disk while TD was closed** (e.g. a landed worktree diff), verify the affected DATs re-synced into the live network BEFORE saving: table DATs load their file only on the post-launch refresh sweep, which can run AFTER an early save -- and the portable export captures LIVE DAT state, shipping stale content (observed v6.0.133: the exported `palette_catalog` was missing all 267 just-landed 33070 rows). Pulse `op.Embody.par.Refresh` and spot-check the changed DATs (row counts, code markers), then save. If you've already mis-saved: rename the off-by-one `.toe` on disk to match `par.Version`, then have the user close TD without saving and reopen. Do **not** save again -- the hook will delete the just-correct release `.tox`. ## 1. Audit All Changes - Run `git diff --stat` and `git diff HEAD --name-status` to identify every changed, added, and deleted file. - Read the diffs for all core source files (EmbodyExt.py, TDXNExt.py, EnvoyExt.py, etc.) to understand what was fixed/added. - Read diffs for new test files to understand coverage additions. - Read diffs for docs, schema, and rule/skill files. ## 1b. Docs Audit: every change in this release is documented The changelog says a release HAPPENED; `docs/` is what users read to USE the thing. A feature or a behavior change that ships with only a changelog bullet is undocumented -- and the drift is invisible, because nothing fails. Walk the release diff (step 1) and, for EVERY user-visible change, name the `docs/` page that now describes it. Not "does a page exist" -- does the page say the new truth. | Change in the diff | Docs obligation | |---|---| | New feature, parameter, MCP tool, or Convoy operation | A section on its owning page, reachable from `mkdocs.yml` nav. A genuinely new subsystem gets its own page. | | Changed behavior (defaults, gating, retries, timeouts, statuses) | Update the page that states the OLD behavior. Grep the old wording -- it is usually in more than one place (a parameter row, a concept page, a troubleshooting row). | | New user-visible string (status text, dialog, error) | Add or fix its Troubleshooting row so a user who searches the literal text lands on the fix. | | Removed / retired feature, par, or tool | Delete or amend every page that still promises it. A stale promise is worse than a missing page. | | Parameter help text edited in TD | `docs/embody/parameters.md` mirrors par help -- keep them identical. | | Internal refactor with no user-visible effect | Nothing. Say so explicitly in the audit rather than skipping the question. | Mechanics: - `git diff HEAD --name-status` -> for each source file, ask what a USER could observe. If the answer is "nothing", write that down; if it is anything else, the docs edit is part of THIS release, not the next one. - Grep before you write: `grep -rn "<old behavior phrase>" docs/` finds the copies that would otherwise go stale. Fix all of them. - New page -> add it to `mkdocs.yml` nav, or it ships invisible. - `mkdocs build --strict` must pass (broken internal links are errors). It proves the page BUILDS; step 7 is what makes it PUBLISHED. - Docs written in this pass are part of the release commit, so the changelog bullet and the page land together. Report the audit as a short list -- "changed X -> documented at `docs/...`", "changed Y -> internal only" -- so a skipped obligation is visible rather than implied. ## 2. Update Changelog Add a new entry at the top of `docs/changelog.md`: ```markdown ## v5.0.XXX One-line summary of the release themes. - **Feature/fix name.** What changed, in 1-2 sentences. - ... ``` ### Length is a hard cap (standing user directive) **One line of theme. 3-5 bullets, one to two lines each. Under ~160 words.** Nobody reads a wall of bullets, and an exhaustive one reads as AI slop. Twelve changes still get <= 5 bullets: group by theme, and give the leftovers one combined **Fixes.** bullet rather than a bullet each. The detail lives in the commit log and the diff. But **too short is also wrong** -- do not drop a user-facing feature to make the count. A shipped tool, a new install path or a fixed crash each earn their line; only internals get cut. A small release can be one bullet (`v6.0.250` in `docs/changelog.md` is the model); a big one uses all five. **Unreleased intermediate versions fold into the published entry.** `project.save()` bumps the version on every save, so most numbers never ship. Consolidate the whole tag-to-tag range (`git log <lasttag>..HEAD`) into the one entry -- and never leave a shipped feature out of it because it landed under a version that was superseded. Always cut: - incident stories, repro narratives, root-cause retelling - internal mechanisms -- protocol shapes, private names, fallback ladders - a superseded version, beyond one short parenthetical - test counts, beyond `+N tests` folded into a bullet Then COUNT. Over 5 bullets or ~160 words, cut before shipping -- never ship long and offer to trim after. ## 3. Update README.md - **Version badge + minimum-build statements are AUTOMATED**: `project.save()` (via `execute_src_ctrl.updateVersionDocs`) rewrites the README version badge from `par.Version` and the minimum-TD-build lines in README.md, docs/index.md, and CONTRIBUTING.md from the running `app.build` (the build we save with IS the support floor). Verify they match rather than editing by hand; the `test_version_sync` suite fails on any drift. - **Release history**: Add a one-line entry at the top of the "Recent releases" list. - **Test suite count**: Update the count if new test files were added (count `dev/embody/unit_tests/test_*.py`). ## 4. Verify Template Sync **When updating a rule or skill in `.claude/`, also update the corresponding template DAT in `dev/embody/Embody/templates/` if one exists.** This applies on every edit, not just at release time -- drift between source and template ships stale guidance to user projects. Templates in `dev/embody/Embody/templates/` must stay in sync with their `.claude/` counterparts: | `.claude/` file | Template file | |---|---| | `rules/td-python.md` | `templates/text_rule_td_python.md` | | `rules/parameters.md` | `templates/text_rule_parameters.md` | | `rules/mcp-safety.md` | `templates/text_rule_mcp_safety.md` | | `rules/network-layout.md` | `templates/text_rule_network_layout.md` | | `rules/td-connectivity.md` | `templates/text_rule_td_connectivity.md` | | `rules/multi-session.md` | `templates/text_rule_multi_session.md` | | `rules/worktree-td-safety.md` | `templates/text_rule_worktree_td_safety.md` | | `rules/performance.md` | `templates/text_rule_performance.md` | | `rules/tdxn-economy.md` | `templates/text_rule_tdxn_economy.md` | | `skills/td-api-reference/SKILL.md` | `templates/text_skill_td_api_reference.md` | | `skills/movie-export/SKILL.md` | `templates/text_skill_movie_export.md` | | `skills/parameter-design/SKILL.md` | `templates/text_skill_parameter_design.md` | | `skills/td-recovery/SKILL.md` | `templates/text_skill_td_recovery.md` | | `skills/multi-session-etiquette/SKILL.md` | `templates/text_skill_multi_session_etiquette.md` | | `skills/create-operator/SKILL.md` | `templates/text_skill_create_operator.md` | | `skills/debug-operator/SKILL.md` | `templates/text_skill_debug_operator.md` | | `skills/externalize-operator/SKILL.md` | `templates/text_skill_externalize.md` | | `skills/create-extension/SKILL.md` | `templates/text_skill_create_extension.md` | | `skills/manage-annotations/SKILL.md` | `templates/text_skill_manage_annotations.md` | | `skills/mcp-tools-reference/SKILL.md` | `templates/text_skill_mcp_tools_reference.md` | | `skills/pop-networks/SKILL.md` | `templates/text_skill_pop_networks.md` | | `skills/visual-aesthetics/SKILL.md` | `templates/text_skill_visual_aesthetics.md` | | `skills/brief/SKILL.md` | `templates/text_skill_brief.md` | | `skills/merge-divergent-tox/SKILL.md` | `templates/text_skill_merge_divergent_tox.md` | | `skills/glsl-shaders/SKILL.md` | `templates/text_skill_glsl_shaders.md` | | `skills/operator-gotchas/SKILL.md` | `templates/text_skill_operator_gotchas.md` | | `skills/testing/SKILL.md` | `templates/text_skill_testing.md` | | `skills/collab/SKILL.md` | `templates/text_skill_collab.md` | | `skills/td-api-reference/references/background-work.md` | `templates/text_skill_td_api_reference__background_work.md` | | `skills/td-api-reference/references/heavy-build-safety.md` | `templates/text_skill_td_api_reference__heavy_build_safety.md` | | `skills/parameter-design/references/state-lifetimes.md` | `templates/text_skill_parameter_design__state_lifetimes.md` | | `skills/mcp-tools-reference/references/coordination.md` | `templates/text_skill_mcp_tools_reference__coordination.md` | | `skills/visual-aesthetics/references/craft.md` | `templates/text_skill_visual_aesthetics__craft.md` | | `skills/visual-aesthetics/references/look-recipes.md` | `templates/text_skill_visual_aesthetics__look_recipes.md` | | `skills/merge-divergent-tox/references/procedure.md` | `templates/text_skill_merge_divergent_tox__procedure.md` | This table is the source of truth for what ships; keep it in sync with `_TEMPLATE_MAP_RULES` / `_TEMPLATE_MAP_SKILLS` in `EmbodyExt.py` (the actual shipping map). Template files that exist on disk but are NOT in that map (e.g. `text_rule_commit_push_checklist.md`, `text_rule_github_release.md`, `text_rule_refresh_after_commit.py`) are orphans -- do not add them here. Templates should be UTF-8 with LF line endings and no BOM. Each template carries an Embody/Envoy generated-by HTML comment, and otherwise must match its `.claude/` counterpart in content -- diff them (normalizing any legacy BOM + line endings) and fix any drift. Dev-only rules and skills (e.g. `.claude/rules/commit-push-checklist.md`, `.claude/rules/skill-prerequisites.md`, `.claude/rules/code-brevity.md`, `.claude/rules/multi-agent-review.md`, `.claude/rules/destructive-tests.md`, `.claude/rules/embody-code-conventions.md`, `.claude/rules/refresh-after-commit.md`, `.claude/skills/release/` (this skill, incl. `references/github-release.md`), `.claude/skills/agent-tests/`, `.claude/skills/add-mcp-tool/`, `.claude/skills/run-tests/`) live under `.claude/` for Embody developers only and are NOT shipped to user projects -- they have no template counterpart. The root `CLAUDE.md` and `dev/embody/Embody/templates/text_claude.md` serve different audiences and are maintained independently. ## 4b. Re-Vendor the Convoy Host App The Convoy host-app daemon exists TWICE on purpose: the source of truth in `dev/convoy/`, and a vendored copy at `dev/embody/Embody/convoy/host/` carried inside the `.tox` as text DATs so that installing works with no network access. If the vendored copy is stale, the release ships an installer that writes an OLD daemon -- and nothing about the running project looks wrong. ``` python dev/convoy/vendor_host_modules.py --check # exit 1 = drift python dev/convoy/vendor_host_modules.py # re-vendor, then SAVE ``` The vendored DATs are `syncfile=True`, so copying the file is the whole re-vendor -- TD reloads the DAT on its own. **The new content only reaches the shipped `.tox` on the next `project.save()`**, so re-vendor BEFORE step 0's save, never after. `--check` reports four drift classes; only the first is fixed by copying: | Class | Meaning | Fix | |---|---|---| | `STALE` | content differs | re-vendor (this script) | | `MISSING` | daemon module has NO vendored DAT | create a text DAT of that name in the `host` COMP and externalize it (tag `py`) -- **a file copy alone does not make it ship** | | `ORPHAN` | vendored file with no daemon source | `delete_op` the DAT (deleting only the file lets the next save re-create it) | | `ok` | current | -- | `test_convoy_host_vendor.py` asserts the same thing on the CI matrix, comparing newline-normalized text (Embody writes CRLF on Windows; `.gitattributes` stores LF, so the committed bytes are identical). Do not "fix" a CRLF diff by changing how Embody writes files -- that path is shared by every externalized DAT in the project. Note `convoy_install.HOST_MODULES` is a hardcoded manifest and has gone stale twice already. It is not the gate; the parity test is. If you add a daemon module, the test tells you what else to do. ## 5. Fresh-Install Smoke (before the release commit) Cold-open smoke of the DEV project is not enough: a fresh install runs a different path (the shipped `.tox` in a virgin project -- `init()` lifecycle, baked par values, no dev checkout, no externalized files). The v6.0.145 empty-Update-Status miss shipped precisely because this step was skipped. Run it after step 0's save (it reads the manifest that save wrote), from the repo root -- one command, same on Windows and macOS: ``` dev/.venv-tests/Scripts/python.exe dev/release_testing/smoke_run.py
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看