Thanks for your interest in improving this extension. This guide covers the development workflow, including the slightly-tricky submodule layout that exists because this extension is consumed by the parent Powernode platform.
This repo is mounted into powernode-platform at extensions/system/. Most
real-world testing requires the parent platform running so the Rails
autoloader sees the extension's namespaces (System::*, Api::V1::System::*).
powernode-platform/ ← parent (separate repo)
├── server/ ← parent's Rails app
├── frontend/ ← parent's React app
├── extensions/
│ └── system/ ← THIS repo (submodule)
│ ├── server/ ← extension's Rails models / services
│ ├── frontend/ ← extension's React components
│ ├── worker/ ← extension's Sidekiq jobs
│ └── ...
# Clone the parent platform with submodules
git clone --recurse-submodules https://github.com/nodealchemy/powernode-platform.git
cd powernode-platform
# Or if already cloned without submodules:
git submodule update --init --recursive
# Set up the extension's dev branch in the submodule
cd extensions/system
git checkout master # or develop, if you have a branch strategy
git remote -v # verify origin (Gitea) + githubFrom the parent platform's server/ directory (so the autoloader sees
the parent's app code):
# Backend rspec
cd /path/to/powernode-platform/server
bundle exec rspec ../extensions/system/server/spec/
# Frontend type-check
cd ../extensions/system/frontend
# (or use the platform's tsconfig.check.json if you've created one)
npx tsc --noEmit
# Go agent
cd ../agent
go test ./...This is the part that bites everyone the first time:
-
Always commit inside
extensions/system/first. From the parent platform's perspective, your changes look like a submodule pointer change until you've committed inside the submodule.cd extensions/system git checkout -b my-feature # ... make changes ... git add server/... git commit -m "feat: add foo" git push origin my-feature
-
Then update the parent's submodule pointer:
cd .. # back to parent platform root git add extensions/system git commit -m "Bump system extension to <sha>"
-
Open a PR in this repo (the system extension), and a separate PR in the parent platform pointing at your new SHA.
| Layer | Rule |
|---|---|
| Ruby | # frozen_string_literal: true pragma, Rails.logger (no puts) |
| TypeScript | No any, no console.log in production code, theme classes only (bg-theme-*) |
| Go | gofmt, prefer the internal/ layout for non-public packages |
| YAML | 2-space indent, no tabs |
| Migrations | Use t.references with built-in indexes; never add_index for FKs separately |
This is non-negotiable in the platform's frontend: check permissions, never roles.
// ✅ correct
currentUser?.permissions?.includes('system.modules.update')
// ❌ wrong — frontend doesn't see role objects
currentUser?.roles?.includes('admin')Backend uses current_user.has_permission?('name').
- All new services + controllers need rspec coverage
- Frontend specs use Vitest + React Testing Library
- E2E flows go in
frontend/cypress/e2e/(parent platform's cypress)
PRs that touch FleetAutonomyService, the AI Skill executors, or anything in
server/db/migrate/ get extra scrutiny:
- Migrations must be reversible (provide
down) - New autonomy actions need:
ACTION_PERMISSIONSentry, intervention policy default, dedup_key_for case, ADVANCEMENT_ACTIONS membership decision - New AI skills need a descriptor + a spec covering plan-vs-execute split
The docs/ tree has a deliberate structure. Pick the right home for
new content; readers don't want to guess.
| Where | What goes there |
|---|---|
docs/tutorials/ |
Numbered, dependency-aware learning sequence. Each tutorial declares Builds on: and Sets you up for: — link the chain. |
docs/runbooks/ |
Day-2 operator procedures. One workflow per doc. Update docs/runbooks/README.md with a row in the table when adding. |
docs/ (root) |
Reference docs — architecture, subsystem deep-dives, design rationale. |
docs/federation/ |
Federation-specific subsystem reference. |
docs/history/ |
Archived phase plans + acceptance reports. Don't write new content here — move existing docs here when they become historical (see "Archive convention" below). |
Diagrams in this repo render natively on both Gitea (≥ v1.17) and the GitHub mirror. Always author Mermaid as text in fenced code blocks:
```mermaid
sequenceDiagram
actor Op
Op->>Server: request
Server-->>Op: response
```Never commit rendered images (SVG, PNG) of diagrams. Text is reviewable in PRs, evolves with code, and stays accessible to screen readers.
The docs/.verify/RENDER_PARITY.md
document captures which Mermaid features render identically on both
targets. When adding a diagram that uses an unusual feature, re-test
on both and update the parity doc.
docs/SKILL_EXECUTOR_CATALOG.md is auto-generated by:
cd server && bundle exec rails system:skills:generate_catalogNever hand-edit this file. Changes will be overwritten on the next regeneration. To document a new skill, update the executor source + re-run the catalog generator.
Phase plans, acceptance reports, and shipped-backlog writeups go under
docs/history/ once the work they describe is complete. Process:
-
git mvthe doc todocs/history/<original-subpath>(preserves history) -
Add the archived banner immediately after the title:
> **ARCHIVED — historical record only.** > This document captures point-in-time state from a prior phase and is no longer > maintained. For current state see <relative paths to current docs>. > _Archived YYYY-MM-DD as part of <reason>._
-
Update
docs/history/README.mdwith a row in the table -
Remove any active-doc links to the archived path (broken links inside
docs/history/itself are fine — the shelf's README is meant to link into the archive)
Phase reports under docs/history/federation/phase-reports/ move
automatically after the next major phase ships.
Three read-only bash scripts under docs/.verify/ check link correctness
- code path references + MCP action names before pushing doc changes:
bash docs/.verify/check-links.sh # every [text](path) resolves
bash docs/.verify/check-code-refs.sh # every cited code path exists
bash docs/.verify/check-mcp-actions.sh # every MCP action exists in registrySee docs/.verify/README.md for output format
and CI integration notes.
For bugs in the extension itself: open issues here on GitHub. For bugs in the parent platform's integration with this extension: open in powernode-platform.
By contributing, you agree your contributions are licensed under MIT (see LICENSE).