name: Sync to HuggingFace Space # hf-sync: keep the HuggingFace Space in lockstep with canonical GitHub main on # szl-holdings/killinchu, split into two INDEPENDENT concerns that each trigger # only on their own changes: # # * README sync (sync-readme job) — fires on README.md (or this workflow) # pushes. Pushes the Space card (README.md) VERBATIM, byte-for-byte. The # GitHub README already carries the full HF Space front-matter (enforced by # readme-frontmatter-check.yml), so no front-matter strip/re-inject. A # SAFETY GUARD refuses a blind push that would sharply shrink the card # (new < old * MIN_RATIO); override an intentional reduction via # workflow_dispatch force=true. # # * App-file mirror (mirror-app job) — fires ONLY on pushes that touch the # deployed app files listed in the APP_FILES env below (serve.py, Dockerfile, # and the route modules). A README-only push no longer re-pushes app code or # forces a no-op Space commit. After mirroring, this job triggers a # HuggingFace factory rebuild via the HF API so the new code actually goes # live with no manual step. Idempotent: identical content is a no-op. # # A lightweight detect job computes which file groups changed (git diff, no # third-party actions — org policy allows only github-owned/verified actions) # and gates the two jobs. workflow_dispatch runs both. # # SINGLE SOURCE OF TRUTH for the mirrored app files: the workflow-level # `env.APP_FILES` below. Both the detect gate and the mirror-app job read it, so # adding/removing a mirrored route module is a one-word change there. GitHub # requires `on.push.paths` to be a LITERAL list (it cannot reference env/vars), # so the trigger list must be kept in lockstep with APP_FILES BY HAND — adding a # route module = one line in on.push.paths + one word in APP_FILES. # # Credential: HF_WRITE_TOKEN (HuggingFace write token with org-write to # SZLHOLDINGS). The legacy HF_TOKEN secret is retired. on: push: branches: [main] paths: - "README.md" - ".github/workflows/hf-sync.yml" # ---- app files: KEEP IDENTICAL to env.APP_FILES below (hf-sync-paths-guard enforces). ---- - "serve.py" - "Dockerfile" - "operator_shell_v4.py" - "szl_evidence_research.py" - "szl_readiness.py" - "szl_quantum_bio.py" - "szl_unified_formulas.py" - "szl_cuas_formulas.py" - "killinchu_research_sources.py" - "szl_contracting.py" - "szl_conjecture_factory.py" - "killinchu_backend.py" - "a11oy_hf_assets.py" - "killinchu_protocols.py" - "killinchu_expansion.py" - "killinchu_naval_haps.py" - "szl_dsse.py" - "szl_provenance.py" - "szl_live_wires.py" - "szl_rosie_companion.py" - "killinchu_szl_pqc_sign.py" - "szl_rekor.py" - "killinchu_osint.py" - "szl_be_hardening.py" - "szl_unay.py" - "szl_khipu_lmdb.py" - "szl_khipu_replicate.py" - "szl_unay_routes.py" - "szl_warhacker_aliases.py" - "killinchu_genius.py" - "killinchu_warhacker_demos.py" - "killinchu_v3.py" - "szl_brain.py" - "szl_rag.py" - "szl_formulas.py" - "szl_understudy.py" - "szl_killinchu_cookbook.py" - "szl_uds_hardening.py" - "killinchu_fusion.py" - "szl_v4_fleet.py" - "szl_ken.py" - "killinchu_frontier_patch.py" - "killinchu_drone_routes.py" - "killinchu_parity.py" - "killinchu_cannonico.py" - "killinchu_elite_console.py" - "_vendor_blobs.py" - "killinchu_fleet_vessels.py" - "killinchu_maritime_risk.py" - "killinchu_maritime_intel.py" - "killinchu_maritime_view.py" - "killinchu_live_feeds.py" - "killinchu_feeds_realdata.py" - "killinchu_asw.py" - "killinchu_anatomy.py" - "killinchu_health_twin.py" - "killinchu_beyond.py" - "szl_khipu_consensus.py" - "killinchu_mesh.py" - "killinchu_mesh_view.py" - "killinchu_formula_endpoints.py" - "killinchu_edge_formulas.py" - "killinchu_active_flux.py" - "killinchu_platform_dynamics.py" - "killinchu_edge_console.py" - "szl_agentic_loop.py" - "szl_anatomy_routes.py" - "szl_formula_wiring.py" - "a11oy_code_engine.py" - "killinchu_ops_control.py" - "szl_llm_registry.py" - "szl_alloy_models.py" - "a11oy_agent_loop.py" - "a11oy_org_rag.py" - "a11oy_mcp_client.py" - "killinchu_mined_ops.py" - "killinchu_resweep_ops.py" - "killinchu_wave910.py" - "killinchu_posture_topology.py" - "szl_connectors_serve.py" - "szl_connector_mcp.py" - "szl_scaling.py" - "szl_allodial.py" - "szl_entanglement.py" - "szl_neuroplasticity.py" - "szl_chain_of_title.py" - "killinchu_mosaic.py" - "szl_mosaic_core.py" - "killinchu_autonomy.py" - "killinchu_autonomy_view.py" - "killinchu_organism.py" - "killinchu_organism_view.py" - "szl_energy_sovereign.py" - "szl_joules_truth.py" # SAPA: Energy per Successful Goal — the frontier agentic unit on top of the live # MEASURED joules/token path. szl_sapa.py is the shared accounting layer (byte- # identical a11oy<->killinchu); szl_sapa_patch.py front-inserts /sapa + # /api/killinchu/v1/sapa/*. KEEP IDENTICAL to env.APP_FILES below (paths-guard enforces). - "szl_sapa.py" - "szl_sapa_patch.py" - "szl_kc_tda_fracture.py" - "a11oy_autoreview.py" - "szl_conformal.py" - "szl_calibration.py" - "szl_hf_bucket.py" - "szl_metrics_prom.py" - "killinchu_maritime_globe.py" - "killinchu_elite_wiring.py" - "killinchu_flow_compartments.py" - "szl_codename_gate.py" - "szl_ecosystem_routes.py" - "szl_restraint.py" - "killinchu_restraint_tile.py" workflow_dispatch: inputs: force: description: "Bypass the anti-shrink guard for an intentional large README reduction" type: boolean default: false permissions: contents: read env: # SINGLE SOURCE OF TRUTH for the mirrored app files (space-separated). The # detect gate and the mirror-app job both read this. Adding a route module is a # one-word change here (plus the matching literal line in on.push.paths above). APP_FILES: "serve.py Dockerfile operator_shell_v4.py szl_evidence_research.py szl_readiness.py szl_quantum_bio.py szl_unified_formulas.py szl_cuas_formulas.py killinchu_research_sources.py szl_contracting.py szl_conjecture_factory.py killinchu_backend.py a11oy_hf_assets.py killinchu_protocols.py killinchu_expansion.py killinchu_naval_haps.py szl_dsse.py szl_provenance.py szl_live_wires.py szl_rosie_companion.py killinchu_szl_pqc_sign.py szl_rekor.py killinchu_osint.py szl_be_hardening.py szl_unay.py szl_khipu_lmdb.py szl_khipu_replicate.py szl_unay_routes.py szl_warhacker_aliases.py killinchu_genius.py killinchu_warhacker_demos.py killinchu_v3.py szl_brain.py szl_rag.py szl_formulas.py szl_understudy.py szl_killinchu_cookbook.py szl_uds_hardening.py killinchu_fusion.py szl_v4_fleet.py szl_ken.py killinchu_frontier_patch.py killinchu_drone_routes.py killinchu_parity.py killinchu_cannonico.py killinchu_elite_console.py _vendor_blobs.py killinchu_fleet_vessels.py killinchu_maritime_risk.py killinchu_maritime_intel.py killinchu_maritime_view.py killinchu_live_feeds.py killinchu_feeds_realdata.py killinchu_asw.py killinchu_anatomy.py killinchu_health_twin.py killinchu_beyond.py szl_khipu_consensus.py killinchu_mesh.py killinchu_mesh_view.py killinchu_formula_endpoints.py killinchu_edge_formulas.py killinchu_active_flux.py killinchu_platform_dynamics.py killinchu_edge_console.py szl_agentic_loop.py szl_anatomy_routes.py szl_formula_wiring.py a11oy_code_engine.py killinchu_ops_control.py szl_llm_registry.py szl_alloy_models.py a11oy_agent_loop.py a11oy_org_rag.py a11oy_mcp_client.py killinchu_mined_ops.py killinchu_resweep_ops.py killinchu_wave910.py killinchu_posture_topology.py szl_connectors_serve.py szl_connector_mcp.py szl_scaling.py szl_allodial.py szl_entanglement.py szl_neuroplasticity.py szl_chain_of_title.py killinchu_mosaic.py szl_mosaic_core.py killinchu_autonomy.py killinchu_autonomy_view.py killinchu_organism.py killinchu_organism_view.py szl_energy_sovereign.py szl_joules_truth.py szl_sapa.py szl_sapa_patch.py szl_kc_tda_fracture.py a11oy_autoreview.py szl_conformal.py szl_calibration.py szl_hf_bucket.py szl_metrics_prom.py killinchu_maritime_globe.py killinchu_elite_wiring.py killinchu_flow_compartments.py szl_codename_gate.py szl_ecosystem_routes.py szl_restraint.py killinchu_restraint_tile.py" jobs: detect: runs-on: ubuntu-latest outputs: readme: ${{ steps.filter.outputs.readme }} app: ${{ steps.filter.outputs.app }} steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 0 lfs: false - name: Detect which file groups changed id: filter env: EVENT: ${{ github.event_name }} BEFORE: ${{ github.event.before }} SHA: ${{ github.sha }} run: | set -euo pipefail # A manual run cannot diff against a previous commit; run both concerns. if [ "$EVENT" = "workflow_dispatch" ]; then echo "readme=true" >> "$GITHUB_OUTPUT" echo "app=true" >> "$GITHUB_OUTPUT" echo "workflow_dispatch: running both README sync and app mirror." exit 0 fi base="${BEFORE:-}" # First push to a branch (all-zero before) or a missing base commit: # fall back to the immediate parent, then to the single-commit view. if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ] \ || ! git cat-file -e "${base}^{commit}" 2>/dev/null; then base="$(git rev-parse "${SHA}^" 2>/dev/null || true)" fi if [ -n "$base" ]; then changed="$(git diff --name-only "$base" "$SHA")" else changed="$(git show --name-only --pretty=format: "$SHA")" fi echo "Changed files:" echo "$changed" readme=false app=false while IFS= read -r f; do [ -z "$f" ] && continue case "$f" in README.md|.github/workflows/hf-sync.yml) readme=true ;; esac # Match against the single-source APP_FILES list (space-separated). for af in $APP_FILES; do [ "$f" = "$af" ] && app=true done done <<< "$changed" echo "readme=$readme" >> "$GITHUB_OUTPUT" echo "app=$app" >> "$GITHUB_OUTPUT" echo "Decision: readme=$readme app=$app" sync-readme: needs: detect if: needs.detect.outputs.readme == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 1 lfs: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Install huggingface_hub run: pip install --quiet "huggingface_hub>=0.25" - name: Sync README verbatim (with anti-shrink safety guard) env: HF_TOKEN: ${{ secrets.HF_WRITE_TOKEN }} SPACE_ID: SZLHOLDINGS/killinchu FORCE: ${{ inputs.force }} MIN_RATIO: "0.85" run: | set -euo pipefail if [ -z "${HF_TOKEN:-}" ]; then echo "::error::HF_WRITE_TOKEN secret is not set — cannot push to the HuggingFace Space." echo "::error::Founder action: add repo secret HF_WRITE_TOKEN (HF write token with org-write to SZLHOLDINGS)." exit 1 fi python3 <<'PYEOF' import os, sys from huggingface_hub import HfApi, CommitOperationAdd, hf_hub_download # HF's server-side README YAML validator occasionally returns a non-JSON # body (HTML/5xx) and raises, aborting an otherwise-valid commit. The # front-matter is well-formed (it is identical to what the Space already # serves), so make the validator non-fatal. _orig = HfApi._validate_yaml def _safe(self, content, *a, **k): try: return _orig(self, content, *a, **k) except Exception as e: print("::warning::HF _validate_yaml skipped (non-fatal):", repr(e)[:160]) return None HfApi._validate_yaml = _safe token = os.environ["HF_TOKEN"] space = os.environ["SPACE_ID"] force = os.environ.get("FORCE", "").lower() == "true" min_ratio = float(os.environ.get("MIN_RATIO", "0.85")) with open("README.md", "rb") as fh: new = fh.read() # Front-matter sanity: a verbatim push must still be a valid Space card. head = new[:4096] if not new.startswith(b"---") or b"\nsdk:" not in head: print("::error::Incoming README is missing YAML front-matter or the 'sdk:' field; refusing to push (would break the Space).") sys.exit(1) api = HfApi(token=token) # Fetch the current Space README for the anti-shrink guard. old = b"" try: p = hf_hub_download(repo_id=space, repo_type="space", filename="README.md", token=token) with open(p, "rb") as fh: old = fh.read() except Exception as e: print("::warning::Could not fetch current Space README (treating as new):", repr(e)[:160]) if old and new == old: print("Space README already byte-identical (%d bytes); nothing to push." % len(new)) sys.exit(0) if old and not force and len(new) < len(old) * min_ratio: print("::error title=anti-shrink guard tripped::Refusing blind push.") print("::error::Incoming README is %d bytes vs %d on the Space (%.1f%% of current; floor is %.0f%%)." % (len(new), len(old), 100.0 * len(new) / len(old), min_ratio * 100)) print("::error::This looks like an accidental content deletion. If the reduction is intentional, re-run with workflow_dispatch force=true.") sys.exit(1) commit = api.create_commit( repo_id=space, repo_type="space", operations=[CommitOperationAdd(path_in_repo="README.md", path_or_fileobj=new)], commit_message="docs: sync Space card with GitHub README (verbatim, byte-identical)", commit_description=("Automated README sync from szl-holdings/killinchu main via hf-sync.\n" "Verbatim push; anti-shrink guard floor=%.2f." % min_ratio), ) print("HF commit:", commit.oid, "->", space, "(%d bytes)" % len(new)) PYEOF # ADDITIVE: the README-only sync above cannot make an app route live. This # job mirrors the deployed backend from canonical GitHub main to the Space so # the Space code never drifts behind GitHub. It runs ONLY when one of the # env.APP_FILES changes (the detect gate above), but the set it actually # mirrors + sweeps is the FULL backend the image is built from — every .py the # Dockerfile COPYs, plus serve.py and the Dockerfile — exactly like the sibling # szl-holdings/a11oy hf-sync-backend. env.APP_FILES is the curated trigger list # (kept in lockstep with on.push.paths); it is NOT the keep-set. # # delete-aware (matches a11oy): besides pushing only the files whose content # differs from the Space (so an unchanged set is a true no-op), it also DELETES, # in the SAME commit, any backend .py present on the Space but no longer in the # computed mirror set (Dockerfile COPY + serve.py) — so a module removed from # the repo + Dockerfile COPY set stops lingering in the Space tree. Deletion is # scoped to backend .py ONLY: it never touches README, the front-door # pages/web HTML+JS, the built SPA bundles, or LFS/vendor blobs (static/, # static-vendor/). The delete keep-set is the FULL Dockerfile-COPY set (NOT the # curated APP_FILES subset) precisely so a real module is never deleted. # # After a real commit it triggers a HuggingFace factory rebuild # (api.restart_space factory_reboot=True) so the mirrored code actually goes # live with no manual step. Idempotent: an in-sync repo makes no commit and # triggers NO rebuild. # # The push/sweep logic lives in .github/scripts/hf_sync_backend.py (extracted so # the SAME code is exercised by the network-free self-test # .github/scripts/test_hf_sync_backend.py, run by hf-sync-backend-selftest.yml). mirror-app: needs: detect if: needs.detect.outputs.app == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 1 lfs: false - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Install huggingface_hub run: pip install --quiet "huggingface_hub>=0.25" - name: Mirror backend (.py + Dockerfile) to the Space — delete-aware env: HF_WRITE_TOKEN: ${{ secrets.HF_WRITE_TOKEN }} SPACE_ID: SZLHOLDINGS/killinchu run: | set -euo pipefail if [ -z "${HF_WRITE_TOKEN:-}" ]; then echo "::error::HF_WRITE_TOKEN secret is not set — cannot mirror app files to the Space." echo "::error::Founder action: add repo secret HF_WRITE_TOKEN (HF write token, org-write to SZLHOLDINGS)." exit 1 fi python3 .github/scripts/hf_sync_backend.py