Background: post-2026-05-06 SCM is Gitea, not GitHub. Gitea 1.22.6 has
no repository_dispatch / workflow_dispatch trigger API (empirically
verified across 6 candidate paths in molecule-core#20 issuecomment-913).
The molecule-core/publish-runtime.yml cascade therefore cannot fire
templates via curl-dispatch — pivots to push-mode instead.
This PR is the consumer side of that pivot:
- `.runtime-version` file at repo root — single line, plain version
string. Currently 0.1.129 (latest published as of 2026-05-07).
publish-runtime overwrites this on each cascade.
- publish-image.yml gains a `resolve-version` job that reads the file
and forwards the value to the reusable build workflow as the
third-priority source in the resolution chain:
1. client_payload.runtime_version (forward-compat with future
GitHub-style dispatch if Gitea ever adds it)
2. inputs.runtime_version (manual workflow_dispatch override)
3. .runtime-version file (push-mode cascade — the new path)
4. '' (Dockerfile requirements.txt default)
No behavioural change for PRs / manual dispatches; only fills in the
on-push case where previously the version was empty.
Sequencing context: this PR (and 8 sibling PRs to the other template
repos) MUST land before molecule-core#20 v2 is merged — otherwise the
first cascade push would trigger an on-push rebuild that pins the OLD
requirements.txt floor instead of the freshly-published version.
Refs molecule-core#14, molecule-core#20, molecule-core/issues/20.
The 3-line wrapper at .github/workflows/secret-scan.yml referenced
`uses: molecule-ai/molecule-core/.github/workflows/secret-scan.yml@staging`.
molecule-core is private; act_runner clones cross-repo reusable
workflows anonymously, so the resolve fails at 0s with no logs.
Same root cause + same fix that molecule-controlplane already shipped
(see its secret-scan.yml comment block lines 10-22). Inlining keeps
the gate functional until Gitea is upgraded or the canonical scanner
moves to a public repo. When either lands, this file reverts to the
3-line wrapper.
Refs: internal#46 Phase 3 Class 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gitea is case-sensitive on owner slugs; canonical is lowercase
`molecule-ai/...`. Mixed-case `Molecule-AI/...` refs fail-at-0s
when the runner tries to resolve the cross-repo workflow / checkout.
Same fix as molecule-controlplane#12. Mechanical case-correction;
no behavior change beyond making CI resolve again.
Refs: internal#46
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without pyyaml in CI, adapter._load_providers' broad except-Exception
swallows the ImportError and silently falls back to _BUILTIN_PROVIDERS.
Tests then assert 7 providers but get 2; setup() can't route any
third-party model. Locally pyyaml is system-installed so the issue
went unnoticed.
Same failure mode as the 2026-04-30 incident (CI green, prod broken)
— pinning the dep here closes that gap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 2026-04-30 staging incident traced back to workspaces booting with
ANTHROPIC_BASE_URL pointing at a non-Anthropic shim (MiniMax / OpenAI
gateway) but no explicit model configured. The adapter silently fell
back to "sonnet" — an Anthropic-native alias the upstream didn't
recognize — and the SDK --print probe hung 30s before timing out.
Platform's phantom-busy sweep then nuked the workspace at 10min,
producing "every workspace dead" with the root cause buried in a
30s subprocess hang.
Pre-validate the combo at adapter boot: when ANTHROPIC_BASE_URL host
is non-Anthropic AND no explicit model is set, raise ValueError with
an actionable message pointing to MODEL_PROVIDER / runtime_config.model.
Also log the resolved model + base_url_host every boot so future
failures explain themselves in the workspace logs without digging
into the SDK subprocess.
Tests live under tests/ with their own pytest.ini that anchors rootdir
there — keeps pytest from importing the package __init__.py (which
does the runtime-discovery relative import that requires
molecule_runtime installed). 7 tests cover: misconfig raises with the
right message, Anthropic-native passes, no-base-url passes, custom-url
+ explicit model passes, dataclass + dict shapes, unparseable URL
no-crash. CI runs them on every push/PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`on: pull_request:` was the only template-repo with this trigger
out of the 8 (other 7 trigger only on push:main, repository_dispatch,
workflow_dispatch). The reusable publish-template-image workflow has
no PR-skip guard, so the PR trigger fired every time a PR was opened
or updated and pushed both `:latest` (clobbering the production tag
with unmerged code) and `:sha-<7>` (a stable tag for an unmerged
commit) to GHCR.
Verification at PR time already happens via the
validate-workspace-template workflow's "Docker build smoke test"
step, which builds the image but does NOT push. That's the right
place for PR-time verification.
Removing the trigger here aligns claude-code with the canonical 7
templates and stops the GHCR leak.
While here, updated the runtime_version comment to drop the now-
stale "/PR" reference.
Closes the cache trap structurally (instead of pin-bumping every
runtime release):
1. publish-image.yml caller now forwards
github.event.client_payload.runtime_version (set by cascade) to
the molecule-ci reusable workflow as runtime_version input.
2. Reusable workflow forwards it to docker build as a --build-arg.
3. Dockerfile declares ARG RUNTIME_VERSION near the pip install
layer so its value becomes part of the cache key.
4. The pip install RUN command does an extra targeted upgrade to
the exact version when ARG is set — guarantees the version is
what we expect even if requirements.txt resolves to something
else.
Pairs with molecule-ci PR #12 + molecule-core PR #2181. Together
the pipeline is now race- and cache-proof end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 'repository_dispatch' trigger (event-type: runtime-published) so
molecule-core's publish-runtime.yml cascade job can fire this template's
image rebuild after a new molecule-ai-workspace-runtime PyPI release.
Without this, every runtime release waited for the next push: main /
manual workflow_dispatch to propagate to the published image. With it,
runtime fixes flow monorepo → PyPI → all 8 template images
automatically.
Part of the runtime CD chain. See molecule-core docs/workspace-runtime-package.md.
Co-authored-by: Hongming Wang <hongmingwangalt@gmail.com>
Branch protection on main requires the publish / Build & push template
image check to pass for all PRs. The workflow previously only triggered
on push to main, so PRs could never satisfy branch protection.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The .auth-token file committed in b8859da contains a live API key.
Remove it from git history and add CI publish-image workflow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>