Imported from AOT-Technologies/m8flow (
AGENTS.md). Install upstream withnpx skills add AOT-Technologies/m8flow. Copyright stays with the author.
AGENTS.md
Project Context
This repository is m8flow, which extends and customizes SpiffArena through patches and extension code.
The project depends on SpiffArena-related folders that may exist locally for development, but they are not owned by this repository:
spiff-arena-common/spiffworkflow-backend/spiffworkflow-frontend/
These folders are imported/reference dependencies and must be treated as upstream/vendor code.
Hard Rules
- Do not modify files under:
spiff-arena-common/spiffworkflow-backend/spiffworkflow-frontend/
- Do not create commits that include changes to those folders.
- Do not reformat, rename, move, or “clean up” files in those folders.
- If a change appears necessary in upstream SpiffArena code, explain the required change instead of editing it directly.
- Prefer implementing behavior through M8Flow extension code, patches, wrappers, configuration, or repo-owned modules.
Repository Ownership
Only modify files that belong to the m8flow repository.
Typical safe areas include:
extensions/- M8Flow-specific backend code
- M8Flow-specific frontend code
- M8Flow-specific patches
- M8Flow configuration
- tests owned by this repo
- documentation owned by this repo
When unsure whether a file is owned by this repo, stop and explain the uncertainty before changing it.
Architecture Guidance
M8Flow is built on top of SpiffArena, not as a fork where upstream folders should be edited directly.
Changes should preserve the patch-based architecture:
- Keep custom behavior isolated in M8Flow-owned extension layers.
- Avoid coupling new code unnecessarily to upstream internals.
- Do not duplicate large sections of upstream code unless there is a clear reason.
- Prefer small, targeted patches over broad rewrites.
- Preserve compatibility with upstream SpiffArena where practical.
Upstream Copy / License Boundary
The imported SpiffArena folders (spiffworkflow-backend/, spiffworkflow-frontend/,
spiff-arena-common/) are LGPL-2.1 and gitignored. The m8flow-owned trees
(m8flow-backend/, m8flow-frontend/, extensions/, etc.) are Apache-2.0.
Do not copy upstream source into the Apache-2.0-tracked trees.
- Do not paste upstream file bodies into m8flow-owned files. A frontend override must carry only the tenant/RBAC delta and wrap the upstream component via the override resolver, not fork the whole upstream file.
- For backend models, preserve the functional contract (column names/types, table names, exported API — these are not copyrightable expression) but re-express the surrounding boilerplate independently (own structure/comments).
- Never carry over upstream attribution comments (author handles,
sartography/URLs) or LGPL/GPL license header text into the Apache-2.0 trees. - CI enforces this with two complementary gates (see
.github/workflows/ci.yml):bin/check-upstream-copying.py— raw-line similarity, cross-language and comment-aware, gated againstbin/upstream-copy-baseline.json.bin/check-upstream-cpd.py— PMD CPD token-level detection that resists reformatting and identifier renaming, gated againstbin/upstream-cpd-baseline.json. Both block new copying and regressions of already-flagged files; neither forces an immediate rewrite of pre-existing copies. License/attribution markers are never grandfathered. Job wiring and usage are documented in.github/workflows/README.md; the flagged files themselves are listed in the two baseline JSONs.
- If you intentionally and reviewably change an already-flagged file, regenerate
the relevant baseline (
--write-baseline) and have the diff reviewed.
Practical Rules To Avoid Copy-Gate Failures
- Default to composition over copying:
- Frontend: wrap upstream components/pages via
@spiff-coreor the override resolver and keep only the M8Flow-specific delta in the repo-owned file. - Backend: patch or wrap the upstream service/controller behavior instead of restating the upstream function body in a repo-owned file.
- Frontend: wrap upstream components/pages via
- Do not copy upstream prop/type boilerplate just to preserve compatibility.
Prefer deriving contracts from the wrapped upstream export when possible
(for example
ComponentProps<typeof UpstreamComponent>in frontend wrappers). - If an override needs extra UI data such as tenant labels, move that logic into small repo-owned helpers/hooks/components rather than cloning the full upstream page or table.
- For shell entrypoints and startup scripts, do not keep the same step order, helper names, comments, and final command layout as the upstream script. Re-express the script in an M8Flow-native structure even when the runtime behavior is similar.
- Do not assume that renaming identifiers, reformatting, or deleting a few lines is enough. The CPD gate is token-based and the raw-line gate also checks contiguous copied blocks and containment.
- Before finalizing any change that touches a repo-owned wrapper/override or a
script resembling an upstream script, run the local copy checks when feasible:
python bin/check-upstream-copying.py --diff origin/mainpython bin/check-upstream-cpd.py
- Treat baseline updates as a last resort, not a routine fix. First try to shrink the override/script until the new finding disappears.
Keycloak Login UX
- Do not change the Keycloak login experience to a two-step username-then-password flow.
- For both the
m8flowrealm and themasterrealm, the login page must collect username and password on the same page. - If you touch Keycloak themes, browser flows, realm imports, or bootstrap scripts, preserve single-page login by keeping
Username Password Formactive and preventing username-only / identity-first login steps from becoming the user-facing path unless explicitly requested. - Do not rely on the upstream/base Keycloak
login-usernamepage for normal sign-in. Repo-owned theme logic must keep the effective sign-in UX on one page. - After Keycloak login/theme/flow changes, verify both realm login pages still render combined username and password fields before considering the work complete.
Multi-Tenancy and RBAC
Be careful with tenant and permission-related behavior.
- Preserve tenant isolation.
- Do not bypass tenant scoping.
- Do not remove or weaken RBAC checks.
- Ensure tenant IDs such as
m8f_tenant_idare handled explicitly where required. - Be cautious around login, group assignment, permissions, human task assignment, and database queries.
- Do not validate shared-realm auth or RBAC changes only with
adminorsuper-admin. - After changes to login, token handling, tenant selection, organization membership sync, or permission patches, verify at least one non-admin shared-realm user such as
editororreviewer. - The minimum protected-route regression check for a non-admin shared-realm user is:
GET /v1.0/onboardingGET /v1.0/tasks
- When touching request-time token or membership refresh code, add or update a route-level test for a stale local shared-realm user and a thin token that must be enriched back into the correct tenant-scoped groups.
- Shared-realm regressions must include the multi-organization case, not just the single-organization case. A user such as
editorjoining a second Keycloak organization must still be able to accessGET /v1.0/onboardingandGET /v1.0/tasksafter tenant selection/finalization. - Do not treat a token as authoritative for shared-realm RBAC refresh merely because it lists organization memberships. For multi-organization users, the active organization’s local groups must be present, or the token must be enriched from Keycloak before tenant-scoped group sync runs.
- In shared-realm multitenant flows, do not treat frontend
localStoragetenant values as authoritative tenant finalization. The backend relies on them8flow_selected_tenantcookie for active-tenant resolution, so UI gates must not bypass tenant selection just because a stale tenant alias remains in browser storage.
Database and Migrations
- Do not make destructive schema changes without clearly explaining the risk.
- Alembic migrations must be reversible where practical.
- Preserve existing data unless the task explicitly requires a data migration.
- Consider PostgreSQL as the primary supported database unless stated otherwise.
Testing and Verification
When changing backend code, consider running or updating relevant tests.
When changing frontend code, consider lint/build impact.
After applying code changes, run the relevant repo-owned checks for the area you touched whenever feasible:
- Backend changes:
- Run the Python lint target for repo-owned backend code (
ruffinm8flow-backend) when backend Python files change. - Run the most relevant
pytesttarget for the touched backend files. - Prefer focused tests first, then widen only if the change is broad or cross-cutting.
- Run the Python lint target for repo-owned backend code (
- Frontend changes:
- Run
npm run lintinm8flow-frontend. - Run
npm testinm8flow-frontend. - Run
npm run buildinm8flow-frontendwhen UI, routing, bundling, or shared frontend infrastructure changed.
- Run
- CI or workflow changes:
- Sanity-check the modified workflow file and, when practical, run the same local commands the workflow is intended to execute.
- Docker, Keycloak, or startup-script changes:
- Run the relevant shell syntax checks and/or
docker compose ... configvalidation when applicable.
- Run the relevant shell syntax checks and/or
- E2E/browser tests:
- These are not part of the default required verification for now.
- Only run them when the user explicitly asks, when the task specifically targets browser automation, or when unit/build checks are insufficient for the risk.
Before finalizing work, summarize:
- What changed
- Which files were changed
- What was intentionally not changed
- Any tests or checks run
- Any remaining risks or assumptions
Dependency Rules
- Do not add new dependencies unless necessary.
- Explain why a new dependency is needed.
- Prefer existing project patterns and libraries.
Git Hygiene
- Keep changes focused.
- Avoid unrelated formatting changes.
- Do not include generated files unless required.
- Do not modify imported SpiffArena folders even if they appear in the working tree.