GloriousFlywheel JS Bazel Package V2 Contract Spec 2026-04-17

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:

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:

  1. explicit repo-owned self-hosted package path
  2. hybrid repo with self-hosted package path and hosted non-package exception
  3. hosted template consumer that may or may not later promote

New Policy Inputs

runner_mode

Allowed values:

  • hosted
  • shared
  • repo_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:

  • isolated
  • persistent_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_runner
  • hosted_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_versions
  • publish_node_version
  • pnpm_version
  • metadata_check_command
  • prepare_command
  • lint_command
  • typecheck_command
  • unit_test_command
  • integration_test_command
  • build_command
  • package_check_command
  • bazel_targets
  • package_dir
  • npm_access
  • npm_publish_provenance
  • github_package_name
  • github_package_registry
  • dry_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 isolated mode

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-kit and acuity-middleware

Workspace Semantics

workspace_mode=isolated

Recommended behavior:

  1. checkout normally
  2. stage work into a per-job directory under $RUNNER_TEMP
  3. install dependencies and build there
  4. 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: false plus 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_compat available

But:

  • document that legacy inference is transitional

Phase B: Migrate Pilot Repos

Pilot repos should explicitly pass:

  • runner_mode
  • workspace_mode
  • publish_mode where needed

Phase C: Tighten Defaults

After pilots are stable:

  • require explicit runner_mode
  • default workspace_mode to isolated
  • stop treating hosted fallback as invisible

scheduling-kit

  • runner_mode=repo_owned
  • workspace_mode=isolated
  • publish_mode=same_runner

acuity-middleware

  • runner_mode=repo_owned
  • workspace_mode=isolated
  • publish_mode=same_runner

Note:

  • deploy-modal.yml stays outside this template and remains a separate hosted exception

tinyvectors

Initial honest setting:

  • runner_mode=hosted
  • workspace_mode=isolated

Promotion options later:

  • shared
  • or repo_owned

Guardrails

Guardrail 1

Fail if:

  • runner_mode=repo_owned
  • and runner_labels_json is empty

Guardrail 2

Warn or fail if:

  • workspace_mode=isolated
  • and cleanup_paths is still provided

Guardrail 3

Fail if:

  • publish_mode=hosted_exception
  • and artifact handoff steps are not configured

Guardrail 4

Fail if:

  • runner_mode is 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.

GloriousFlywheel