Nightly automation that syncs Autodesk Fusion 360 tool library data into Rockwell Automation Plex
Manufacturing Cloud (ERP). Fusion 360 JSON files on a local network share are the absolute source
of truth. As of the April 2026 architecture pivot, Fusion data lands first in a Supabase
database (enriched source of truth — geometry, holder pairings, pocket assignments) and then an
identity slice (vendor part number + description) is pushed on to Plex's supply-items endpoint.
The React UI reads from Supabase; Plex gets only what its schema can accept.
| Plex environment | connect.plex.com (production) — there is no test environment for this app |
| Plex app | Datum Consumer Key, expires every 31 days (next rotation: 2026-05-08, issue #12) |
| Plex tenant | 58f781ba-1691-4f32-b1db-381cdb21300c (Grace Engineering) |
| Tooling endpoint | inventory/v1/inventory-definitions/supply-items filtered to category="Tools & Inserts" (1,109 records) |
| Workcenters | production/v1/production-definitions/workcenters (143 records, including 21 mills mapping directly to Brother Speedio FTP IPs) |
| Supabase | dedicated datum project (us-east-2), 3 tables — libraries, tools, cutting_presets |
| Phase | Phase B complete — validate_library.py pre-sync gate landed (#25). Phase A-Python (Supabase upsert layer) is next. |
| Tests | 215 pytest tests, all green. CI on PRs to master via GitHub Actions. Branch protection requires the check to pass. |
Fusion 360 JSON (network share, via Autodesk Desktop Connector)
│
▼
validate_library.py ← pre-sync gate: abort if library is invalid (#25)
│
▼
sync_supabase.py ← upsert full tool records into Supabase [Phase A-Python]
│
├──▶ Supabase (bulletforge) ← enriched source of truth
│ │
│ └──▶ React UI ← tool library browser [Phase D+]
│
└──▶ sync_plex.py ← identity slice only → supply-items [Phase C]
Why the pivot: Plex's supply-items schema is identity-only — vendor part number and description,
nothing else. Geometry, holder pairings, and pocket assignments have no home in Plex. Supabase
holds the full record; Plex gets the slice it can accept.
The original plan to write to mdm/v1/parts and tooling/v1/tool-assemblies was incorrect — see
BRIEFING.md "History of incorrect hypotheses" for the postmortem.
-
Clone and create your
.env.localgit clone https://github.com/grace-shane/datum.git cd datum copy .env.example .env.local # Edit .env.local with your Datum Consumer Key + Secret
.env.localis gitignored. Get the Consumer Key from developers.plex.com → My Apps → Datum. -
Install dependencies
py -m pip install -r requirements-dev.txt
-
Run the local endpoint tester
py run_dev.py
Opens on http://localhost:5000. The left rail has buttons for:
- Diagnostics —
tenant_whoami(run this first to verify connection) - Plex presets — verified Plex API URLs as one-click hits
- Extractors —
extract_supply_items(1,109 cutting tools),extract_parts,extract_purchase_orders, etc. - Fusion 360 local —
tools_statsandconsumables_onlyfor verifying the local Fusion library load
run_dev.pyoverrides shell environment variables with.env.local(the opposite ofbootstrap.py's production-safesetdefaultsemantics), so a stale system env var won't silently shadow your real key. - Diagnostics —
-
Run tests
py -m pytest -
Validate a Fusion library before syncing
# Production mode — PASS/FAIL only, exit code 0 or 1 py validate_library.py --file "BROTHER SPEEDIO ALUMINUM.json" --no-api # Verbose — shows WARN issues too py validate_library.py --file "BROTHER SPEEDIO ALUMINUM.json" --no-api --verbose # With live Plex supplier lookup for VENDOR_NOT_IN_PLEX checks py validate_library.py --file "BROTHER SPEEDIO ALUMINUM.json" --verbose
The validator catches duplicate product-ids, missing required fields, non-positive geometry, unknown tool types, and vendors that won't resolve to a Plex supplier. The sync layer gates on a PASS; FAILs abort the sync before anything touches Supabase or Plex. Full rule table in
docs/validate_library_spec.md.
This codebase reads from real Grace Engineering production data on every API call. Two guard rails protect against accidental writes:
-
PlexClient.get_envelope()returns structured success/error envelopes so HTTP failures are visible (PR #15 fixed an earlier "swallow on error" bug). -
/api/plex/rawproxy refuses POST/PUT/PATCH/DELETE when running againstconnect.plex.comunlessPLEX_ALLOW_WRITES=1is set in the environment (PR #17). Read-only is always allowed. To enable writes:$env:PLEX_ALLOW_WRITES = "1" py run_dev.py
The UI shows a red
WRITES ONchip when the guard is disabled. Rotate the env var off as soon as you're done.
docs/BRIEFING.md— primary context document for AI-assisted dev sessions and the source of truth for current status, current credentials, gotchas, and project historydocs/Plex_API_Reference.md— verified endpoint access matrix and URL pattern conventionsdocs/Fusion360_Tool_Library_Reference.md— Fusion JSON schema and field-to-Plex mappingdocs/validate_library_spec.md— design spec for the pre-sync validation gate; implemented asvalidate_library.py(#25)TODO.md— project roadmap mirrored to GitHub Issues- GitHub Issues — live status of every phase work item with dependencies and blockers
- Plex Manufacturing Cloud API docs
- Branch from
master - Push to a
claude/<short-name>branch (or any branch — naming is convention, not enforced) - Open a PR to
master - CI runs
pytestautomatically - Branch protection blocks merge until the check is green
- Use
gh pr merge --auto --squashto enable auto-merge — it lands the PR the moment CI passes
Internal Grace Engineering project. Forked from
just-shane/plex-api.