Skip to content

feat: workflow/state machine detection — StatusType nodes, TRANSITIONS edges#560

Closed
marxo126 wants to merge 3 commits into
abhigyanpatwari:mainfrom
marxo126:feat/workflow-detection-v3
Closed

feat: workflow/state machine detection — StatusType nodes, TRANSITIONS edges#560
marxo126 wants to merge 3 commits into
abhigyanpatwari:mainfrom
marxo126:feat/workflow-detection-v3

Conversation

@marxo126

Copy link
Copy Markdown
Contributor

Summary

  • Detects status/state type definitions and status transition patterns across multiple languages
  • New StatusType node type with TRANSITIONS edges mapping which functions change which statuses
  • Supports TypeScript unions/enums, Python Enums, Java enums, plus generic assignment and setter patterns

What it detects

Status type definitions

Pattern Languages Example
Union types (single + multi-line) TypeScript type GrantStatus = 'DRAFT' | 'ACTIVE' | 'COMPLETED'
TS/JS enums TypeScript/JS enum OrderStatus { PENDING = 'pending' }
Python Enum classes Python class OrderStatus(Enum): PENDING = 'pending'
Java enums Java enum Status { PENDING, ACTIVE, COMPLETED }

Status transitions

Pattern Scope Example
Prisma ORM updates Prisma prisma.grant.update({ data: { status: 'ACTIVE' } })
Transactional updates Prisma tx.step.update({ data: { status: 'approved' } })
Direct assignment Any language order.status = 'shipped' / self.status = 'active'
Setter patterns Any language entity.setStatus('approved') / obj.set_status('active')
Generic .update() Any ORM entity.update({ status: 'completed' })

Entity-name matching

Transitions matched to StatusType by entity name first (e.g., prisma.grant.update -> GrantStatus), falling back to value-based matching only when entity name doesn't match.

Schema changes

  • New StatusType node table: id, name, filePath, statusValues STRING[], statusKind
  • TRANSITIONS added to REL_TYPES
  • FROM/TO pairs: File/Function/Method -> StatusType, StatusType -> Community/Process
  • COPY query + insert + batch-upsert in lbug-adapter

Real-world validation (Next.js + Prisma, 30K nodes)

Metric Count
StatusType nodes 63
Core domain types found (of 7 expected) 7/7
TRANSITIONS edges 76 (71 direct + 5 transactional)

Known limitations

  1. Status type detection is heuristic. Name pattern /status|state|phase|stage|step|workflow|lifecycle/i OR 2+ status-like values. Produces false positives for UI step types (~35% noise).
  2. Transition detection is regex-based. Complex patterns (dynamic field names, computed values, spread operators) are missed.
  3. Entity-to-type matching relies on naming convention. Non-standard naming falls back to value-based matching, which can misattribute.
  4. Generic assignment may over-match. Any .status = 'value' is detected — including UI state and test mocks.
  5. Python/Java support is basic. Type detection works, but transition detection only covers generic patterns (assignment, setter). No Django ORM, JPA, or ActiveRecord patterns yet.
  6. Pipeline only scans TS/TSX for transitions. Python/Java transition detection is available but not wired into the pipeline yet.
  7. Transition metadata encoded in reason string. fromStatus, toStatus, entityType stored as direct-update:grant:DRAFT->ACTIVE, not separate LadybugDB columns.
  8. No transition validation. Doesn't check if the target value is valid for the StatusType.

Test plan

  • 14 unit tests (union types, enums, Python Enum, Java enum, Prisma, generic assignment, setter)
  • Schema count assertions updated
  • Full test suite passes
  • Real-repo validation: all 7 core types, 76 transitions

🤖 Generated with Claude Code

@vercel

vercel Bot commented Mar 28, 2026

Copy link
Copy Markdown

@marxo126 is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

CI Report

Some checks failed

Pipeline Status

Stage Status Details
❌ Typecheck failure tsc --noEmit
✅ Tests success unit tests, 3 platforms
❌ E2E failure gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
6621 6524 0 97 262s

✅ All 6524 tests passed

97 test(s) skipped — expand for details
  • Swift MethodExtractor > isTypeDeclaration > recognizes class_declaration
  • Swift MethodExtractor > isTypeDeclaration > recognizes protocol_declaration
  • Swift MethodExtractor > isTypeDeclaration > rejects import_declaration
  • Swift MethodExtractor > visibility > extracts public method
  • Swift MethodExtractor > visibility > extracts private method
  • Swift MethodExtractor > visibility > defaults to internal when no modifier
  • Swift MethodExtractor > protocol methods > marks protocol method as abstract
  • Swift MethodExtractor > static and class methods > detects static func as isStatic
  • Swift MethodExtractor > static and class methods > detects class func as isStatic
  • Swift MethodExtractor > parameters > extracts parameters with types and default values
  • Swift MethodExtractor > return type > extracts return type from -> annotation
  • Swift MethodExtractor > annotations > extracts @objc attribute
  • Swift MethodExtractor > isFinal > detects final func
  • Swift MethodExtractor > isFinal > is false when not final
  • Swift MethodExtractor > isAsync > detects async func
  • Swift MethodExtractor > isOverride > detects override method
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift explicit init inference uses lookupClassByName
  • buildTypeEnv > constructor inference (Tier 1 fallback) > lookupClassByName regression coverage > Swift lookupClassByName regression coverage > Swift cross-file constructor inference does not bind plain functions
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()
  • Swift implicit imports (cross-file visibility) > detects UserService class in Models.swift
  • Swift implicit imports (cross-file visibility) > resolves UserService() constructor call across files (no explicit import)
  • Swift implicit imports (cross-file visibility) > resolves service.fetchUser() member call across files
  • Swift implicit imports (cross-file visibility) > creates IMPORTS edges between files in the same module
  • Swift extension deduplication > detects Product class
  • Swift extension deduplication > resolves Product() constructor despite extension creating duplicate class node
  • Swift extension deduplication > resolves product.save() to Product.swift (primary definition)
  • Swift constructor call fallback (no new keyword) > resolves OCRService() as constructor call across files
  • Swift constructor call fallback (no new keyword) > resolves ocr.recognize() member call via constructor-inferred type
  • Swift export visibility (internal vs private) > resolves PublicService() constructor across files
  • Swift export visibility (internal vs private) > resolves internalHelper() across files (internal = module-scoped)
  • Swift if let / guard let binding resolution > detects User and Repo classes
  • Swift if let / guard let binding resolution > resolves user.save() inside if-let to User#save
  • Swift if let / guard let binding resolution > resolves repo.save() inside guard-let to Repo#save
  • Swift if let / guard let binding resolution > user.save() in if-let does NOT resolve to Repo#save
  • Swift await / try expression unwrapping > resolves user.save() via await fetchUser() return type
  • Swift await / try expression unwrapping > resolves repo.save() via try parseRepo() return type
  • Swift await / try expression unwrapping > detects fetchUser and parseRepo as functions
  • Swift for-in loop element type inference > detects User and Repo classes
  • Swift for-in loop element type inference > creates implicit import edges between files
  • Swift field-type resolution > detects classes and their properties
  • Swift field-type resolution > emits HAS_PROPERTY edges from class to field
  • Swift field-type resolution > resolves field-chain call user.address.save() → Address#save
  • Swift field-type resolution > emits ACCESSES edges for field reads in chains
  • Swift field-type resolution > populates field metadata (visibility, declaredType) on Property nodes
  • Swift call-result binding > resolves call-result-bound method call user.save() → User#save
  • Swift call-result binding > getUser() is present as a defined function
  • Swift call-result binding > emits processUser -> getUser CALLS edge for let-assigned free function call
  • Swift method enrichment > detects Animal protocol and Dog class
  • Swift method enrichment > emits IMPLEMENTS edge Dog -> Animal
  • Swift method enrichment > emits HAS_METHOD edges for Dog methods
  • Swift method enrichment > marks protocol Animal.speak as isAbstract
  • Swift method enrichment > marks Dog.speak as NOT isAbstract
  • Swift method enrichment > marks breathe as isFinal
  • Swift method enrichment > marks classify as isStatic
  • Swift method enrichment > captures @objc annotation on breathe
  • Swift method enrichment > populates parameterTypes for classify(_ name: String)
  • Swift method enrichment > records parameterCount for classify
  • Swift method enrichment > records returnType for speak
  • Swift method enrichment > resolves dog.speak() CALLS edge
  • Swift method enrichment > resolves Dog.classify("dog") CALLS edge
  • Swift abstract dispatch > detects Repository protocol and SqlRepository class
  • Swift abstract dispatch > emits IMPLEMENTS edge SqlRepository -> Repository
  • Swift abstract dispatch > emits HAS_METHOD edges for Repository.find and Repository.save
  • Swift abstract dispatch > emits HAS_METHOD edges for SqlRepository.find and SqlRepository.save
  • Swift abstract dispatch > marks base Repository.find as isAbstract
  • Swift abstract dispatch > marks base Repository.save as isAbstract
  • Swift abstract dispatch > marks concrete SqlRepository.find as NOT isAbstract
  • Swift abstract dispatch > resolves repo.find(id: 42) CALLS edge
  • Swift abstract dispatch > resolves repo.save(entity: user) CALLS edge
  • Swift abstract dispatch > populates parameterTypes for Repository.find
  • Swift abstract dispatch > populates parameterTypes for Repository.save
  • Swift abstract dispatch > records returnType for SqlRepository.find
  • Swift abstract dispatch > emits METHOD_IMPLEMENTS edges from SqlRepository methods → Repository protocol methods
  • Swift overloaded method disambiguation > detects 2 distinct find Method nodes on SqlRepository
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edges for both find overloads
  • Swift overloaded method disambiguation > emits METHOD_IMPLEMENTS edge for save
  • Swift overloaded method disambiguation > emits exactly 3 METHOD_IMPLEMENTS edges total
  • Swift Child extends Parent — inherited method resolution (SM-9) > detects Parent and Child classes
  • Swift Child extends Parent — inherited method resolution (SM-9) > resolves c.parentMethod() to Parent.parentMethod via first-wins MRO walk

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 73.2% 17987/24571 73.22% 📉 -0.0 🔴 ██████████████░░░░░░
Branches 62.13% 11398/18343 62.24% 📉 -0.1 🔴 ████████████░░░░░░░░
Functions 77.83% 1703/2188 77.82% 📈 +0.0 🟢 ███████████████░░░░░
Lines 75.79% 16318/21529 75.78% 📈 +0.0 🟢 ███████████████░░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

Please make sure to include your changes in the gitnexus-shared folder 🙏 I moved types over there so we can share them between cli and web.

@magyargergo

Copy link
Copy Markdown
Collaborator

⚠️ Upcoming Prettier formatting — rebase instructions

PR #563 adds Prettier as the code formatter for the repo. When it merges, the bulk format commit will touch ~350 files (style-only: whitespace, quotes, trailing commas). Your branch will likely conflict.

After #563 merges, rebase your branch:

git fetch origin
git checkout <your-branch>
git rebase origin/main

# Conflicts will be formatting-only — accept your version:
git checkout --theirs .
git add .
git rebase --continue

# Then re-format your branch to match the new style:
npx prettier --write .
git add -A
git commit -m "style: apply prettier formatting"
git push --force-with-lease

New setup step: Run npm install at the repo root (not just in gitnexus/) to get prettier + activate the pre-commit hook. The hook auto-formats staged files on every commit going forward.

@marxo126

Copy link
Copy Markdown
Contributor Author

Done — latest push includes gitnexus-shared changes (StatusType in NodeLabel + NODE_TABLES, TRANSITIONS in RelationshipType + REL_TYPES, new GraphRelationship props). Also ran Prettier formatting.

@marxo126 marxo126 force-pushed the feat/workflow-detection-v3 branch 2 times, most recently from a9a8fbc to b5a9a79 Compare April 1, 2026 10:46
Test and others added 3 commits April 18, 2026 13:11
…S edges

Detect status type definitions (union types, enums) and status transition
patterns (Prisma .update, direct assignments, setters) across TS/JS/Python/Java.
Creates StatusType nodes with statusValues/statusKind properties and TRANSITIONS
edges with fromStatus/toStatus/entityType/isTransactional metadata.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…used imports

Adds StatusType entry to both NODE_COLORS and NODE_SIZES Record objects
in gitnexus-web to satisfy the NodeLabel type constraint. Removes unused
DetectedStatusType and DetectedTransition type imports from test file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@marxo126 marxo126 force-pushed the feat/workflow-detection-v3 branch from c849eb3 to c988e7a Compare April 18, 2026 11:14
@magyargergo

Copy link
Copy Markdown
Collaborator

Please submit a new PR if this is still relevant

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