Skip to content

ci: central orchestrator + auto-generated deps graph#154

Closed
TeoSlayer wants to merge 4 commits into
mainfrom
orchestrator-deps-graph
Closed

ci: central orchestrator + auto-generated deps graph#154
TeoSlayer wants to merge 4 commits into
mainfrom
orchestrator-deps-graph

Conversation

@TeoSlayer

Copy link
Copy Markdown
Collaborator

Summary

This PR consolidates the cascade system into one mechanism: the orchestrator. Closes the chapter on the hardcoded shockwave fan-out.

What changed

Cascade

  • release.yml shockwave job → replaced with a single notify-orchestrator step that emits package-released. The orchestrator does the rest.
  • New orchestrator.yml (from prior commit) receives package-released events, reads .github/deps.json, computes reverse-transitive closure by depth, dispatches per-target bump-upstream events in topological order.
  • Adding/removing a sibling no longer touches release.yml — the graph is data, not code.

Auto-generated deps graph

  • scripts/build-deps.py parses live go.mod (+ package.json, Package.swift) from every pilot-protocol/* repo + TeoSlayer/pilotprotocol and produces .github/deps.json (27 nodes, every edge).
  • refresh-deps-graph.yml runs the generator hourly, on manual dispatch, and on repository_dispatch deps-touched. If the regen produces a diff against the checked-in file, opens (or updates) a PR. The graph cannot silently drift.
  • Hand-maintained input is now just .github/deps.policy.json — bump policy per node, freeloader markings, manual nodes for repos with no manifest (homebrew, website).

Receiver templates (from prior commit)

  • _template-emit-release.yml — what each sibling installs to notify the orchestrator on its own release.
  • _template-bump-upstream.yml — what each sibling installs to receive a bump dispatch (go mod edit + go mod tidy + commit/PR per policy).

Closure tests (passing)

push common         → 24 targets (everyone transitively)
push gateway        →  1 target  (web4)
push dataexchange   →  2 targets (web4, examples)
push trustedagents  →  6 targets (handshake, runtime, webhook, libpilot, ...)
push handshake      → 11 targets (runtime, webhook, libpilot, sdks, ...)
push libpilot       →  3 targets (3 SDKs)
push sdk-node       →  0 targets (leaf)
push cosift         →  0 targets (freeloader)

Required setup

  • SHOCKWAVE_DISPATCH_TOKEN secret on this repo with repository_dispatch scope on every sibling + this repo itself. Prefer a GitHub App (actions/create-github-app-token@v1) over a PAT.

Supersedes / closes

Test plan

  • Manually workflow_dispatch orchestrator.yml with package: common, version: v0.2.0, dry_run: true and verify the closure JSON.
  • Then re-run with dry_run: false to fire actual dispatches (once token is set).
  • Tag a no-op release on web4 (v1.10.6-rc1) and verify notify-orchestrator step fires correctly.

🤖 Generated with Claude Code

teovl added 4 commits May 28, 2026 12:50
Drop a TODO at the top of release.yml enumerating which secrets need to
be re-created on `pilot-protocol/` before the repo transfer, since
GitHub secrets do not survive a repo transfer.

Currently the only expected secret is GITHUB_TOKEN (auto-issued).
HOMEBREW_TAP_TOKEN was removed in #122 when update-homebrew.yml was
dropped, and NPM_TOKEN / PYPI_TOKEN / COSIGN_KEY are placeholders for
the auto-publish (PILOT-203) and binary-signing (PILOT-114) work that
hasn't landed yet.

This is documentation only — no behavior change. The comment block is
load-bearing for the org migration; deleting it before the new org has
its secrets configured will silently break the next release.
Two new jobs run after the existing release job:

publish-manifest builds public/.well-known/latest.json from the tag's
checksums.txt, then repository_dispatches it to pilot-protocol/website,
which commits the JSON to main and triggers the Cloudflare deploy. The
single canonical manifest at pilotprotocol.network/.well-known/latest.json
is consumed by install.sh, the Homebrew formula bump workflow, and the
SDK release helpers — one shockwave per release.

shockwave fans out repository_dispatch(event_type=upstream-release)
to homebrew-pilot, sdk-node, sdk-python, and sdk-swift so each consumer
can run its own bump workflow. Per-target dispatch is soft-fail with a
summary so a missing token on one repo does not block the others.

Both jobs require a new SHOCKWAVE_DISPATCH_TOKEN secret with
repository_dispatch scope on each downstream repo (prefer a GitHub App
token over a PAT). When the secret is absent the steps emit a clear
::warning:: and exit 0 so existing release flow is not broken.
…ver templates

Adds a central release orchestrator that handles bidirectional dependency
propagation: when any package in the constellation ships a release, the
set of downstream nodes that need a bump is computed automatically and
each gets a dispatch in topological order.

Files:

  .github/deps.json
    Canonical dependency graph. 25 nodes: hub (web4), 15 Go siblings, 1
    Go app, 1 FFI fan-in (libpilot), 3 SDKs, 1 brew tap, 1 surface
    (website), 3 freeloaders (cosift, pilot-ca, wallet). Each node
    declares its depends_on edges and a bump_policy (stable_only,
    auto_commit_main, open_pr_instead_of_main). This is the single
    source of truth — changes to the graph are PR-reviewed diffs here.

  .github/workflows/orchestrator.yml
    Receives package-released dispatches from any node. Computes the
    reverse-transitive closure of the released package (BFS by depth
    in deps.json), filters per-receiver stable_only when the upstream
    is a prerelease, then fan-outs bump-upstream dispatches to each
    affected repo. Supports workflow_dispatch with a dry_run flag for
    rehearsal.

  .github/workflows/_template-emit-release.yml
    Reusable workflow that any sibling adopts: at the end of its own
    release.yml it calls this with the node name, and it dispatches
    package-released to the orchestrator. One token per sibling
    (ORCHESTRATOR_DISPATCH_TOKEN) covers the hop.

  .github/workflows/_template-bump-upstream.yml
    Generic receiver template for Go-sibling repos: receives
    bump-upstream, runs go mod edit -require=<module>@<version> +
    go mod tidy + go build sanity check, then either commits to main
    or opens a PR based on BUMP_MODE. SDK repos will need their own
    package-manager-specific variants.

Closure smoke-tests pass:

  web4      → 22 targets (every depending node, ordered by depth)
  policy    → 4 targets  (libpilot + 3 SDKs)
  handshake → 4 targets  (libpilot + 3 SDKs)
  libpilot  → 3 targets  (3 SDKs)
  sdk-node  → 0 targets  (leaf)
  cosift    → 0 targets  (freeloader)

This SUPERSEDES the direct shockwave fan-out in PR #151's release.yml
once orchestrator adoption rolls out — the four hardcoded repos there
become entries in deps.json instead. Keeping #151's direct fan-out for
now as the bootstrap path (orchestrator needs SHOCKWAVE_DISPATCH_TOKEN
set before it can do anything).
…eceiver templates

Replaces the hardcoded shockwave fan-out in release.yml with a single
notify-orchestrator step that emits package-released. The orchestrator
workflow reads .github/deps.json (now auto-generated by
scripts/build-deps.py from real go.mod / package.json / Package.swift)
and computes the reverse-transitive closure of the released package,
then dispatches per-target bump-upstream events in topological order.

Why this is better than the hardcoded fan-out:

  - Adding/removing a sibling no longer touches release.yml; just create
    or delete the repo and the next deps refresh picks it up.
  - The graph is always in sync with reality. scripts/build-deps.py
    parses live go.mod files; refresh-deps-graph.yml runs hourly and on
    repository_dispatch deps-touched. When the regen produces a diff
    it opens (or updates) auto/deps-graph-refresh PR.
  - Sibling-to-sibling edges that the hardcoded shockwave missed
    (rendezvous → handshake → libpilot, eventstream → dataexchange,
    etc.) are now caught by including indirect requires.
  - Stable-only policy is honored per-receiver inside the orchestrator
    (the bump dispatch is skipped when upstream is a prerelease AND
    the receiver wants stable only).

Hand-maintained input is now only:
  .github/deps.policy.json — bump policy per node + freeloader markings
                              + manual_nodes for repos with no manifest
                              (homebrew-pilot, website, freeloaders).

Files in this commit:

  scripts/build-deps.py                       generator (gh + raw fetch)
  .github/deps.json                           auto-output, 27 nodes
  .github/deps.policy.json                    hand-maintained sidecar
  .github/workflows/orchestrator.yml          unchanged from prior commit
  .github/workflows/refresh-deps-graph.yml    NEW — scheduled regen + PR
  .github/workflows/release.yml               drop shockwave; add
                                              notify-orchestrator step
  .github/workflows/_template-emit-release.yml unchanged
  .github/workflows/_template-bump-upstream.yml unchanged

Required secret: SHOCKWAVE_DISPATCH_TOKEN (repository_dispatch scope on
every sibling + the orchestrator's own repo).
@TeoSlayer

Copy link
Copy Markdown
Collaborator Author

Superseded by pilot-protocol/release. The orchestrator + deps graph + manifest worker now live in a single dedicated repo. This consolidates the cascade machinery cleanly outside web4.

@TeoSlayer TeoSlayer closed this May 29, 2026
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.

2 participants