GloriousFlywheel JS Bazel Package V2 Contract Spec 2026-04-17
Snapshot date: 2026-04-17
Purpose
Define the intended interface and semantics of js-bazel-package V2 before the
template is edited.
This note exists to prevent the V2 rollout from becoming a vague cleanup PR.
Companion notes:
- gloriousflywheel-js-bazel-package-v2-rollout-2026-04-17.md
- gloriousflywheel-contract-rollout-pr-stack-2026-04-17.md
- gloriousflywheel-exception-register-and-promotion-rules-2026-04-17.md
Current V1 Reality
The current template already handles:
- Node and pnpm setup
- build, lint, typecheck, unit and integration commands
- Bazel target validation
- package tarball and publish dry-runs
- real npm and GitHub Packages publish
But the current interface still hides three policy decisions:
- whether the repo is meant to be hosted or self-hosted
- whether the workspace model is isolated or persistent
- whether publication authority is expected to live on the same runner class as validation
V2 should make those decisions explicit.
Consumer Constraints
Verified current consumers:
scheduling-kit- repo-owned dedicated lane
- package path currently self-hosted
acuity-middleware- repo-owned dedicated lane
- package path self-hosted
- deploy path separately hosted
tinyvectors- template consumer
- no current repo-variable proof of self-hosted usage
This means V2 must support three cases:
- explicit repo-owned self-hosted package path
- hybrid repo with self-hosted package path and hosted non-package exception
- hosted template consumer that may or may not later promote
Recommended V2 Inputs
New Policy Inputs
runner_mode
Allowed values:
hostedsharedrepo_owned
Meaning:
hosted- package workflow is intentionally GitHub-hosted
shared- package workflow uses a documented shared GloriousFlywheel lane
repo_owned- package workflow uses repo-specific runner labels
workspace_mode
Allowed values:
isolatedpersistent_compat
Meaning:
isolated- stage work in a per-job scratch directory
- no dependency on persistent checkout state
persistent_compat- retain current cleanup-based model temporarily
publish_mode
Allowed values:
same_runnerhosted_exception
Meaning:
same_runner- publish from the same runner class that validated the package artifact
hosted_exception- publish from GitHub-hosted runners intentionally
- only after validated artifacts are handed off
Existing Low-Level Inputs To Keep
Keep these because they are about package behavior, not platform policy:
node_versionspublish_node_versionpnpm_versionmetadata_check_commandprepare_commandlint_commandtypecheck_commandunit_test_commandintegration_test_commandbuild_commandpackage_check_commandbazel_targetspackage_dirnpm_accessnpm_publish_provenancegithub_package_namegithub_package_registrydry_run
Inputs To Deprecate Or Narrow
runner_labels_json
Keep as a low-level override, but narrow its meaning:
- required when
runner_mode=repo_owned - optional override for
shared - ignored or forbidden for
hosted
cleanup_paths
Current status:
- V1 compatibility input
V2 status:
- allowed only when
workspace_mode=persistent_compat - should be unused in
isolatedmode
Recommended Semantics
runner_mode=hosted
Behavior:
- run on GitHub-hosted labels intentionally
- do not call
setup-flywheel - provenance behavior may remain hosted-first
Primary use:
- explicit hosted template consumers
runner_mode=shared
Behavior:
- run on a documented shared GF label set
- call
setup-flywheel - express cache behavior visibly
Recommended default shared profile for this workflow:
tinyland-docker
This is an implementation recommendation, not a current fact.
Reason:
- the template is Node and Bazel oriented
- it does not inherently require Nix
- the simplest shared package story should not require a Nix lane by default
runner_mode=repo_owned
Behavior:
- require non-empty
runner_labels_json - call
setup-flywheel - fail fast if labels are missing or malformed
Primary use:
- repo-owned package lanes such as
scheduling-kitandacuity-middleware
Workspace Semantics
workspace_mode=isolated
Recommended behavior:
- checkout normally
- stage work into a per-job directory under
$RUNNER_TEMP - install dependencies and build there
- only copy out the validated package artifact
Important property:
- no dependency on old files in the persistent self-hosted checkout path
workspace_mode=persistent_compat
Behavior:
- preserve current
clean: falseplus cleanup-path model
Important property:
- this is a migration bridge, not the preferred contract
Publication Semantics
publish_mode=same_runner
Behavior:
- validate and publish on the same runner class
- use the same artifact lineage
Best fit:
- repos where self-hosted package authority is already real and registry behavior is not blocked
publish_mode=hosted_exception
Behavior:
- validation happens on the chosen self-hosted or hosted build path
- validated artifacts are uploaded
- a hosted publish job downloads and publishes them intentionally
Best fit:
- cases where npm provenance or registry constraints still make hosted publish the honest choice
Compatibility Strategy
Phase A: Introduce V2 Without Breaking Existing Consumers
For the first merged V2 revision:
- allow callers that do not yet pass
runner_mode - infer legacy behavior conservatively
- keep
persistent_compatavailable
But:
- document that legacy inference is transitional
Phase B: Migrate Pilot Repos
Pilot repos should explicitly pass:
runner_modeworkspace_modepublish_modewhere needed
Phase C: Tighten Defaults
After pilots are stable:
- require explicit
runner_mode - default
workspace_modetoisolated - stop treating hosted fallback as invisible
Recommended Pilot Settings
scheduling-kit
runner_mode=repo_ownedworkspace_mode=isolatedpublish_mode=same_runner
acuity-middleware
runner_mode=repo_ownedworkspace_mode=isolatedpublish_mode=same_runner
Note:
deploy-modal.ymlstays outside this template and remains a separate hosted exception
tinyvectors
Initial honest setting:
runner_mode=hostedworkspace_mode=isolated
Promotion options later:
shared- or
repo_owned
Guardrails
Guardrail 1
Fail if:
runner_mode=repo_owned- and
runner_labels_jsonis empty
Guardrail 2
Warn or fail if:
workspace_mode=isolated- and
cleanup_pathsis still provided
Guardrail 3
Fail if:
publish_mode=hosted_exception- and artifact handoff steps are not configured
Guardrail 4
Fail if:
runner_modeis self-hosted- but self-hosted contract setup is skipped
Acceptance Criteria
- V2 makes runner intent explicit
- V2 makes workspace intent explicit
- V2 makes hosted publish exceptions explicit
- the template can serve both dedicated-lane and hosted-template consumers without ambiguity
Recommendation
Treat V2 as an interface change, not a cleanup patch.
If the interface is clear first, the downstream repo migrations become small. If the interface is left implicit, every pilot repo will invent its own local story again.