Skip to content

refactor: extract GoalTree into separate package#6

Merged
vieirin merged 54 commits intomainfrom
generic-goal-tree
Feb 11, 2026
Merged

refactor: extract GoalTree into separate package#6
vieirin merged 54 commits intomainfrom
generic-goal-tree

Conversation

@vieirin
Copy link
Copy Markdown
Owner

@vieirin vieirin commented Jan 28, 2026

Summary

This PR extracts the GoalTree module into a dedicated package and introduces a generic, type-safe engine mapper architecture that enables multiple transformation engines (PRISM/Edge and SLEEC) to coexist with their own typed property systems.

Major Changes

1. New @goal-controller/goal-tree Package

  • Extracted GoalTree functionality into its own package for better separation of concerns
  • Moved core files: creation.ts, dependencyResolver.ts, nodeUtils.ts, printTree.ts, treeVariables.ts, types.d.ts, utils.ts, and parsers
  • GoalTree can now be used independently by other packages

2. Generic Type System with Engine Mapper

  • Made GoalNode, Task, and Resource generic types that accept engine-specific property types
  • Introduced createEngineMapper() factory for type-safe property transformation from raw iStar strings to rich typed structures
  • Added afterCreationMapper hook for cross-node reference resolution (e.g., dependsOn)
  • Supports both goal/task mappers and optional resource mappers with validation

3. Multi-Engine Architecture

  • Restructured packages/lib/src into engines/edge/ and engines/sleec/ directories
  • Each engine has its own:
    • mapper.ts - Property transformation from iStar to engine-specific types
    • types.ts - Engine-specific type definitions
    • template/ - Output generation templates
  • Edge engine includes validator/, logger/, and mdp/ modules

4. Strict TypeScript Mode

  • Enabled strict TypeScript mode for the lib package
  • Added explicit type annotations throughout to eliminate implicit any errors

5. Improved Validation & Error Handling

  • Fail fast when tasks have goal children (invalid model structure)
  • Validate resource properties (bool must be "true"/"false", int bounds validation)
  • Validate choice goals have pursueable children
  • Better error messages with context (goal/task ID, property name)
  • Added logger warnings for missing achievability variables

6. Documentation

  • Added comprehensive Engine Development Tutorial in README
  • Documents the two-phase architecture (Mapper → Template)
  • Includes complete SLEEC engine implementation example
  • Architecture diagrams using Mermaid

Files Changed (119 files)

  • New: packages/goal-tree/ - Complete goal tree package
  • Restructured: packages/lib/src/engines/ - Edge and SLEEC engines
  • Updated: All imports across the codebase
  • Tests: Updated to use new generic types and engine mappers

Breaking Changes

  • GoalNode, Task, and Resource types now require generic parameters
  • Properties accessed via node.properties.engine.* instead of direct properties
  • Must use an engine mapper with GoalTree.fromModel() or GoalTree.fromJSON()

Benefits

  • Type Safety: Engine-specific properties are fully typed at compile time
  • Extensibility: Easy to add new transformation engines
  • Separation of Concerns: Goal tree logic independent from engine implementations
  • Validation: Invalid models fail fast with clear error messages

Summary by CodeRabbit

  • New Features

    • Added a Goal Tree package with public Model and GoalTree APIs for loading, parsing, building, querying, and serializing iStar goal trees.
    • Added two transformation engines (Edge/PRISM and SLEEC) with validated generation of models/specs and CLI support to export JSON trees.
  • Chores

    • Workspace, build/dev scripts and TypeScript/test configs updated; build pipeline now builds the new package.
  • Documentation

    • Transformation engine docs reorganized; legacy READMEs removed.

- Move GoalTree module from packages/lib to packages/goal-tree
- Update imports across the codebase to use the new package
- Remove deleted files from lib (creation.ts, dependencyResolver.ts, etc.)
- Add goal-tree as workspace dependency
- Update sleecTemplateEngine to use new GoalTree package
- Fix implicit any type errors in callback functions
@vercel
Copy link
Copy Markdown

vercel bot commented Jan 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
goal-controller Ready Ready Preview, Comment Feb 11, 2026 0:39am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new workspace package @goal-controller/goal-tree (types, Model, GoalTree SDK, engine mapper, ANTLR artifacts), reorganizes lib into edge/sleec engines (mappers, templates, validators), migrates CLI/UI/tests to use the new public API, updates scripts/configs, and adapts many templates/tests to engine-prefixed properties.

Changes

Cohort / File(s) Summary
Workspace & scripts
package.json, vercel.json, packages/goal-tree/package.json, Makefile
Add @goal-controller/goal-tree workspace; add dev:goal-tree/build:goal-tree scripts; include goal-tree build in dev/build/vercel and minor Makefile fix.
Goal-tree public SDK
packages/goal-tree/src/index.ts, packages/goal-tree/src/GoalTree.ts, packages/goal-tree/src/Model.ts, packages/goal-tree/src/Node.ts
Introduce public Model namespace (load/parse/validate), GoalTree class with factory methods and query interface, Node helpers and exported types.
Types & internals (goal-tree)
packages/goal-tree/src/types/*, packages/goal-tree/src/internal/*, packages/goal-tree/src/internal/creation.ts, packages/goal-tree/src/internal/engineMapper.ts
Add iStar and goal-tree type system, engine-mapper factory (createEngineMapper), convertToTree pipeline, traversal/utils/variables helpers and internal barrel.
ANTLR artifacts (goal-tree)
packages/goal-tree/src/antlr/*
Add generated ANTLR TypeScript lexer/parser/listener files (AssertionRegex, RTRegex) with serialized ATNs and DFA initialization.
TS projects & tests (goal-tree)
packages/goal-tree/tsconfig.json, packages/goal-tree/tsconfig.test.json, packages/goal-tree/.mocharc.json, packages/goal-tree/test/**
Add tsconfigs and mocha config for the new package and tests.
Lib reorg: engines
packages/lib/src/engines/index.ts, packages/lib/src/engines/edge/**, packages/lib/src/engines/sleec/**
Reorganize template engines into engines/edge and engines/sleec; add mappers, types, templates, validators and central engine exports.
Edge engine templates & validator
packages/lib/src/engines/edge/template/**, packages/lib/src/engines/edge/validator/**
Add edgeDTMCTemplate, generateValidatedPrismModel, changeManagerModule and modular template pieces; adapt templates/validators to Edge types and the new GoalTree API.
SLEEC engine templates
packages/lib/src/engines/sleec/**
Add SLEEC mapper, types, template modules (definitions, rules, purposes) adapted to new GoalTree API.
Lib public API & CLI/menu
packages/lib/src/index.ts, packages/lib/src/cli.ts, packages/lib/src/cli/menu/*
Replace internal conversions with public Model.load and GoalTree.fromModel; adapt CLI/menu to load variables, use tree.query, and pass tree.nodes to engines.
Template modules refactor
packages/lib/src/.../template/modules/*, many packages/lib/src/...
Widespread updates: read engine properties under properties.engine, replace internal GoalTree helpers with GoalTree/Node public APIs, and change GoalNode→Edge/Sleec typed usages across many template modules.
Removed / moved code & docs
packages/lib/src/templateEngine/modules/changeManager/*, packages/lib/src/prismValidator/README.md, packages/lib/src/sleecTemplateEngine/README.md, README.md
Remove legacy module(s) and old READMEs; top-level README updated to engine-centric documentation.
Tests & tsconfigs (lib)
packages/lib/test/**, packages/lib/test/tsconfig.json, packages/lib/.mocharc.json
Update tests to use Model.load/GoalTree.fromModel, add test tsconfigs, and adjust mocha/ts-node settings.
Package integrations
packages/lib/package.json, packages/ui/package.json
Add workspace dependency @goal-controller/goal-tree to lib and ui packages; update lib test script to set TS_NODE compiler options.
Tooling & config tweaks
.claude/settings.local.json, .gitignore, .vscode/settings.json
Expand Bash allowlist entries, add tsbuildinfo ignores, change TypeScript default formatter, and other tooling adjustments.
ANTLR updates in lib
packages/lib/src/antlr/*
Reformat/modernize generated ANTLR files and listener signatures in lib; switch to lazy ATN/DFA initialization pattern.
Numerous small type & import updates
many packages/lib/**, packages/ui/** files
Adjust imports, types, and property access to the new GoalTree API and engine-prefixed properties across templates, utilities, and tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant Model
    participant GoalTree
    participant Mapper
    participant Template
    participant Validator
    participant Output

    User->>CLI: submit model (file or JSON)
    CLI->>Model: Model.load() / Model.parse()
    Model-->>CLI: IStarModel
    CLI->>GoalTree: GoalTree.fromModel(model, Mapper)
    GoalTree->>Mapper: convertToTree / mapGoalProps
    Mapper-->>GoalTree: TreeNode[] (with engine props)
    CLI->>Template: generate({ gm: tree.nodes }, variables)
    Template->>GoalTree: query / Node helpers (allByType, children)
    Template->>Validator: validate(gm: tree.nodes, model)
    Validator-->>Template: ValidationReport
    Template-->>Output: generated model string
    Output-->>User: deliver transformed model
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 I hopped through types and parser trees,

Mapped raw props into engine seas,
GoalTree sprouted, engines spun,
Edge and SLEEC now working as one,
A tiny hop — a build begun!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'refactor: extract GoalTree into separate package' is a clear and specific summary of the main change in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch generic-goal-tree

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/lib/src/mdp/common.ts (1)

36-37: Pre-existing bug: AND function uses wrong separator.

The AND function on line 37 uses separator('or') instead of separator('and'). This appears to be a pre-existing bug not introduced by this PR, but it should be fixed.

🐛 Proposed fix
 export const AND = (elements: string[]): string =>
-  elements.join(separator('or'));
+  elements.join(separator('and'));
packages/lib/src/templateEngine/modules/changeManager/template/achievabilityVariables/taskAchievabilityVariables.ts (1)

12-16: Falsy check on numeric value may reject valid 0 values.

!variableValue evaluates to true when variableValue is 0, which could be a legitimate value. Consider checking for undefined or null explicitly.

🐛 Proposed fix
-  if (!variableValue) {
+  if (variableValue === undefined || variableValue === null) {

Or using nullish check:

-  if (!variableValue) {
+  if (variableValue == null) {
🤖 Fix all issues with AI agents
In @.claude/settings.local.json:
- Around line 13-23: The .claude/settings.local.json contains user-specific
absolute paths and overly broad allowed commands (e.g., entries like "Bash(git
-C /Users/vieira.neto/vieirin/goal-controller ...)" and "Bash(find:*)",
"Bash(pnpm test:*)"); remove or replace these user-specific entries with
repo-safe, generic defaults (e.g., non-absolute git commands and constrained
command patterns), move any personal overrides into an ignored local file, and
add .claude/settings.local.json to .gitignore (or rename the tracked file to
.claude/settings.json with sanitized contents) so personal paths/permissions are
not committed for all contributors.

In `@packages/goal-tree/package.json`:
- Around line 1-24: Update the lodash dependency in
packages/goal-tree/package.json by changing the "lodash" entry under
"dependencies" from ^4.17.21 to ^4.17.23, then regenerate the lockfile
(npm/yarn/pnpm install) so the updated version is pinned; ensure the change is
committed before publishing and run your test/build scripts (scripts:
build/watch/clean) to verify no regressions.

In `@packages/goal-tree/src/antlr/AssertionRegexLexer.ts`:
- Around line 1-14: The generated ANTLR lexer AssertionRegexLexer.ts is not
covered by CI regeneration; add a clear regeneration step and docs: update
project CONTRIBUTING or README to document that
packages/lib/grammar/AssertionRegex.g4 is the source and must be regenerated
with ANTLR 4.13.2, add an npm script (e.g., "generate:antlr") that runs the
ANTLR TypeScript generation for AssertionRegex.g4, and add a CI job that runs
this script and fails the build if generated files (like AssertionRegexLexer.ts)
are out of date; reference the generated symbol AssertionRegexLexer and the
grammar file AssertionRegex.g4 in the docs and CI job to make the relationship
explicit.

In `@packages/goal-tree/tsconfig.tsbuildinfo`:
- Line 1: The committed file packages/goal-tree/tsconfig.tsbuildinfo is a
generated TypeScript build artifact and must be removed from version control and
ignored; delete packages/goal-tree/tsconfig.tsbuildinfo from the repo, add an
entry to the repository .gitignore (e.g., tsconfig.tsbuildinfo or
packages/goal-tree/tsconfig.tsbuildinfo) so it isn’t committed again, and commit
the .gitignore change plus the file deletion (use git rm --cached if already
tracked) to stop machine-specific build info from leaking into the repo.

In `@packages/lib/.mocharc.json`:
- Around line 10-19: The .mocharc.json currently mixes CommonJS and ESM configs:
remove the "node-option": ["loader=ts-node/esm"] and the "ts-node" section so
the file relies on the existing "require": ["ts-node/register"] (CommonJS), or
if you intend to move to ESM instead, remove "require": ["ts-node/register"] and
add "type": "module" to package.json and keep the ESM loader; update whichever
path you choose so the config is consistent (reference: "node-option", "ts-node"
entries in .mocharc.json and "require": ["ts-node/register"] and "type":
"module" in package.json).

In `@packages/lib/package.json`:
- Line 14: The test script currently disables strict type checking by setting
TS_NODE_COMPILER_OPTIONS with "strict":false and "noImplicitAny":false; remove
these overrides from the "test" npm script in package.json and instead fix the
failing test typings: run the test suite with the default/strict TS settings,
then update test files (correct any incorrect types, add explicit annotations,
fix missing imports or casts) so that functions and test helpers referenced in
tests (e.g., any custom helpers or mocked functions in the test files) satisfy
TypeScript's strict and noImplicitAny rules; once tests compile under strict
mode, restore the standard test script without TS_NODE_COMPILER_OPTIONS.

In `@packages/lib/test/tsconfig.json`:
- Around line 1-8: test/tsconfig.json is dead/unused duplicate config; either
delete this file or wire it into the Mocha/ts-node setup by referencing it from
.mocharc.json via TS_NODE_PROJECT (or remove TS_NODE_COMPILER_OPTIONS from
package.json and centralize options). Locate the test/tsconfig.json and
.mocharc.json and choose one: if keeping the tsconfig, update .mocharc.json to
set "ts-node": { "project": "test/tsconfig.json" } (or set TS_NODE_PROJECT
accordingly in package.json); otherwise remove test/tsconfig.json and ensure
tests rely only on the existing TS_NODE_COMPILER_OPTIONS settings already used
by .mocharc.json and package.json.
🧹 Nitpick comments (8)
packages/lib/src/templateEngine/decisionVariables.ts (1)

17-25: Remove redundant type annotations and rely on TypeScript inference.

The inline type { variable: string; space: number } is duplicated in lines 17-23. Since GoalNode.decisionVars is already typed as Array<{ variable: string; space: number }> in the Decision type, you can use shorthand destructuring to let TypeScript infer the parameter types.

♻️ Proposed refactor
-  const spaceArray = goal.decisionVars.map(
-    (decision: { variable: string; space: number }) =>
-      Array.from({ length: decision.space }, (_, i) => i),
-  );
-  const variableArray = goal.decisionVars.map(
-    (decision: { variable: string; space: number }) => decision.variable,
-  );
+  const spaceArray = goal.decisionVars.map(({ space }) =>
+    Array.from({ length: space }, (_, i) => i),
+  );
+  const variableArray = goal.decisionVars.map(({ variable }) => variable);
packages/goal-tree/src/treeVariables.ts (1)

4-6: Code duplication with packages/lib/src/templateEngine/common.ts.

This helper is now duplicated in both packages. While this is reasonable to avoid circular dependencies between goal-tree and lib, consider documenting this coupling or extracting shared utilities to a common constants package if the naming convention changes in the future.

packages/lib/src/templateEngine/modules/changeManager/changeManager.ts (1)

3-5: Consider consolidating imports from the same module.

Multiple import statements from @goal-controller/goal-tree can be combined for cleaner code.

♻️ Suggested consolidation
-import { getTaskAchievabilityVariables } from '@goal-controller/goal-tree';
-import type { GoalTree } from '@goal-controller/goal-tree';
-import { allByType } from '@goal-controller/goal-tree';
+import {
+  allByType,
+  getTaskAchievabilityVariables,
+  type GoalTree,
+} from '@goal-controller/goal-tree';
packages/lib/src/templateEngine/modules/goalModule/template/index.ts (1)

5-6: Consider consolidating imports from the same module.

Similar to other files, these can be combined.

♻️ Suggested consolidation
-import type { GoalNode } from '@goal-controller/goal-tree';
-import { childrenIncludingTasks } from '@goal-controller/goal-tree';
+import {
+  childrenIncludingTasks,
+  type GoalNode,
+} from '@goal-controller/goal-tree';
packages/lib/src/templateEngine/modules/goalModule/template/pursue/index.ts (1)

1-2: Consider consolidating imports from the same module.

♻️ Suggested consolidation
-import type { GoalNode } from '@goal-controller/goal-tree';
-import { childrenIncludingTasks } from '@goal-controller/goal-tree';
+import {
+  childrenIncludingTasks,
+  type GoalNode,
+} from '@goal-controller/goal-tree';
packages/lib/src/prismValidator/expectedElements.ts (1)

92-94: Consider using library-provided types instead of inline type annotations.

The inline type { name: string } works but may drift from the actual type definition in @goal-controller/goal-tree. If the library exports a Variable type or similar, importing and using that would provide stronger type safety and automatic updates if the type changes.

packages/goal-tree/src/antlr/RTRegexParser.ts (1)

1-17: Auto-generated ANTLR parser file.

This file is generated from grammar/RTRegex.g4 by ANTLR 4.13.2. As auto-generated code, it should not be manually modified. Consider adding a build step to regenerate these files from the grammar, and potentially adding the generated files to .gitignore to avoid committing generated code.

packages/lib/src/cli.ts (1)

15-17: Consider consolidating imports from the same module.

These three separate imports from @goal-controller/goal-tree can be combined into a single statement for cleaner code.

♻️ Suggested consolidation
-import { loadPistarModel } from '@goal-controller/goal-tree';
-import { convertToTree } from '@goal-controller/goal-tree';
-import { dumpTreeToJSON } from '@goal-controller/goal-tree';
+import {
+  loadPistarModel,
+  convertToTree,
+  dumpTreeToJSON,
+} from '@goal-controller/goal-tree';

Comment on lines +13 to +23
"Bash(git push)",
"Bash(git -C /Users/vieira.neto/vieirin/goal-controller status)",
"Bash(git -C /Users/vieira.neto/vieirin/goal-controller log --oneline -20 -- packages/lib/src/GoalTree/)",
"Bash(git -C /Users/vieira.neto/vieirin/goal-controller log --oneline -10)",
"Bash(find:*)",
"Bash(pnpm run build:goal-tree:*)",
"Bash(tsc:*)",
"Bash(pnpm exec tsc --help:*)",
"Bash(pnpm exec tsc:*)",
"Bash(pnpm run build:ui:*)",
"Bash(pnpm test:*)"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove user-specific/local permissions from the repo.

This file now embeds absolute, user-specific paths and broadens the allow-list for commands in a shared config. That’s non-portable and weakens safety defaults for other contributors. Please keep local overrides untracked (or move shared defaults to a sanitized file and ignore the local one).

✅ Suggested direction
-  "Bash(git -C /Users/vieira.neto/vieirin/goal-controller status)",
-  "Bash(git -C /Users/vieira.neto/vieirin/goal-controller log --oneline -20 -- packages/lib/src/GoalTree/)",
-  "Bash(git -C /Users/vieira.neto/vieirin/goal-controller log --oneline -10)",
-  "Bash(find:*)",
-  "Bash(pnpm run build:goal-tree:*)",
-  "Bash(tsc:*)",
-  "Bash(pnpm exec tsc --help:*)",
-  "Bash(pnpm exec tsc:*)",
-  "Bash(pnpm run build:ui:*)",
-  "Bash(pnpm test:*)"

And ensure .claude/settings.local.json is gitignored (or replace with a repo-safe .claude/settings.json).

🤖 Prompt for AI Agents
In @.claude/settings.local.json around lines 13 - 23, The
.claude/settings.local.json contains user-specific absolute paths and overly
broad allowed commands (e.g., entries like "Bash(git -C
/Users/vieira.neto/vieirin/goal-controller ...)" and "Bash(find:*)", "Bash(pnpm
test:*)"); remove or replace these user-specific entries with repo-safe, generic
defaults (e.g., non-absolute git commands and constrained command patterns),
move any personal overrides into an ignored local file, and add
.claude/settings.local.json to .gitignore (or rename the tracked file to
.claude/settings.json with sanitized contents) so personal paths/permissions are
not committed for all contributors.

Comment on lines +1 to +24
{
"name": "@goal-controller/goal-tree",
"version": "1.0.0",
"description": "Goal tree data structures and conversion utilities",
"main": "out/index.js",
"types": "out/index.d.ts",
"scripts": {
"build": "rm -rf out tsconfig.tsbuildinfo && tsc",
"watch": "tsc --watch",
"clean": "rm -rf out tsconfig.tsbuildinfo"
},
"dependencies": {
"antlr4": "^4.13.0",
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/lodash": "^4.14.197",
"@types/node": "^22.6.0",
"typescript": "^5.8.3"
},
"engines": {
"node": ">=22.6.0"
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

latest npm versions antlr4 lodash 2025 2026

💡 Result:

  • antlr4 (npm package antlr4): 4.13.2 (latest) [2][3]
  • lodash (npm package lodash): 4.17.23 (latest) [1]

Sources: npm registry “latest” for lodash [1], npm package pages / ANTLR site for antlr4 [2][3].


🌐 Web query:

Node.js 22.6.0 LTS release date status

💡 Result:

  • Node.js v22.6.0 release date: 2024-08-06 — released as Current (not LTS). [1]
  • When v22 became LTS: 2024-10-29 (the v22.11.0 release marked the LTS transition to “Active LTS”). [2]
  • Current status of the v22 line (as of 2026-01-28): Maintenance LTS (v22 “Jod”). [3]

Sources: [1] [2] [3]


🌐 Web query:

antlr4 npm security advisories vulnerabilities

💡 Result:

  • antlr4 (npm package): As of Jan 28, 2026, there are no published security advisories in the upstream GitHub repo’s Security tab, and no known direct vulnerabilities reported by Snyk for the latest npm version (4.13.2). [1][2][3]
  • Watch for name-squatting / malicious lookalikes: OSV lists a malicious npm package named antlr4-graps (not antlr4). Don’t confuse it with the official antlr4 runtime. [4]

Sources: [1] GitHub Security overview (antlr/antlr4) • [2] Snyk vulns page for antlr4 • [3] npm package page for antlr4 • [4] OSV entry for antlr4-graps


🌐 Web query:

lodash npm security advisories vulnerabilities 2025

💡 Result:

Lodash (npm) security advisories / vulnerabilities relevant to 2025

As of calendar year 2025, there weren’t widely reported new lodash CVEs disclosed that newly affected the current maintained lodash line; most activity in 2025 was updates/metadata refreshes to older advisories (e.g., GitHub advisory entries updated in 2025). For example, the GitHub advisory for CVE-2020-28500 (ReDoS) shows an update date in Sep 2025, but the issue itself is older and is fixed by upgrading to lodash ≥ 4.17.21. [1]

The “2025” CVE you’ll see in tooling (but published in 2026)

Many scanners will surface CVE-2025-13465 (alias GHSA-xxjr-mmjv-4gpg) for lodash:

  • Issue: Prototype pollution via _.unset / _.omit enabling deletion of properties from global prototypes (deletion, not overwriting).
  • Affected: lodash 4.0.0 – 4.17.22
  • Fixed: lodash 4.17.23
  • Published: Jan 21–23, 2026 (despite the CVE number being “2025”).
    Sources: OSV/GitHub advisory record and NVD description. [2][3] (also summarized in GitLab’s advisory DB) [4]

What to do (practical)

  • If you want to be clear of both the historical issues and CVE-2025-13465, upgrade to lodash ≥ 4.17.23. [2][3][4]
  • Current scanner views generally report no known issues affecting the latest lodash (at time of writing) when on the fixed release. [5]

Sources
[1] GitHub Advisory Database – GHSA-29mw-wpgm-hmr9 / CVE-2020-28500 (updated Sep 29, 2025) (github.com)
[2] OSV – GHSA-xxjr-mmjv-4gpg (published 2026-01-21; aliases CVE-2025-13465; fixed in 4.17.23) (osv.dev)
[3] NVD – CVE-2025-13465 (affected through 4.17.22; patched in 4.17.23) (nvd.nist.gov)
[4] GitLab Advisory DB – CVE-2025-13465 (affected <4.17.23; fixed 4.17.23) (advisories.gitlab.com)
[5] Snyk – lodash package page (latest version shows no vulnerabilities) (security.snyk.io)

Citations:


Upgrade lodash to ^4.17.23 to fix CVE-2025-13465.

The lodash dependency version ^4.17.21 is vulnerable to CVE-2025-13465 (prototype pollution via _.unset / _.omit), which affects versions 4.0.0–4.17.22 and is fixed in 4.17.23. Do not publish until this is resolved.

The antlr4 version (^4.13.0) is current and has no known security advisories. The Node.js engine floor (>=22.6.0) is appropriate.

🤖 Prompt for AI Agents
In `@packages/goal-tree/package.json` around lines 1 - 24, Update the lodash
dependency in packages/goal-tree/package.json by changing the "lodash" entry
under "dependencies" from ^4.17.21 to ^4.17.23, then regenerate the lockfile
(npm/yarn/pnpm install) so the updated version is pinned; ensure the change is
committed before publishing and run your test/build scripts (scripts:
build/watch/clean) to verify no regressions.

Comment on lines +1 to +14
// Generated from grammar/AssertionRegex.g4 by ANTLR 4.13.2
// noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols
import {
ATN,
ATNDeserializer,
CharStream,
DecisionState,
DFA,
Lexer,
LexerATNSimulator,
RuleContext,
PredictionContextCache,
Token,
} from 'antlr4';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the grammar file referenced
fd -type f -name "AssertionRegex.g4"

Repository: vieirin/goal-controller

Length of output: 237


🏁 Script executed:

# Check package.json for build/generate scripts
fd -type f -name "package.json" packages/goal-tree --exec cat {} \;

Repository: vieirin/goal-controller

Length of output: 237


🏁 Script executed:

# Look for CI configuration files
fd -type f \( -name "*.yml" -o -name "*.yaml" -o -name ".github" \) . --max-depth 3

Repository: vieirin/goal-controller

Length of output: 237


🏁 Script executed:

# Check if ANTLR files are mentioned in .gitignore
find . -name ".gitignore" -type f -exec grep -H "antlr\|AssertionRegex" {} \;

Repository: vieirin/goal-controller

Length of output: 94


🏁 Script executed:

# Get the full content of AssertionRegexLexer.ts to check actual usage of imported symbols
wc -l packages/goal-tree/src/antlr/AssertionRegexLexer.ts

Repository: vieirin/goal-controller

Length of output: 122


🏁 Script executed:

# Find the grammar file
find . -name "AssertionRegex.g4" -type f

Repository: vieirin/goal-controller

Length of output: 107


🏁 Script executed:

# Check package.json in packages/goal-tree
cat packages/goal-tree/package.json

Repository: vieirin/goal-controller

Length of output: 644


🏁 Script executed:

# Read the full AssertionRegexLexer.ts file
cat -n packages/goal-tree/src/antlr/AssertionRegexLexer.ts

Repository: vieirin/goal-controller

Length of output: 7291


🏁 Script executed:

# Look for CI configuration
find . -path "./.git" -prune -o -type f \( -name "*.yml" -o -name "*.yaml" \) -print | head -20

Repository: vieirin/goal-controller

Length of output: 210


🏁 Script executed:

# Check for build-related configuration
ls -la .github/workflows/ 2>/dev/null || echo "No GitHub workflows found"

Repository: vieirin/goal-controller

Length of output: 301


🏁 Script executed:

cat .github/workflows/test.yaml

Repository: vieirin/goal-controller

Length of output: 667


🏁 Script executed:

cat .github/workflows/prism-test.yaml

Repository: vieirin/goal-controller

Length of output: 1308


🏁 Script executed:

# Check if there's a root-level script or documentation for ANTLR regeneration
find . -maxdepth 2 -type f \( -name "Makefile" -o -name "*.md" \) -exec grep -l -i "antlr" {} \;

Repository: vieirin/goal-controller

Length of output: 96


ANTLR-generated lexer file — add regeneration documentation.

This is auto-generated code from packages/lib/grammar/AssertionRegex.g4 by ANTLR 4.13.2. The structure follows the standard ANTLR TypeScript lexer pattern.

Note: The noinspection comment on line 2 correctly suppresses IDE warnings for the imports. While Token is actually used on line 31 (Token.EOF), the RuleContext import is genuinely unused but remains typical in ANTLR-generated files. Do not manually edit this file.

⚠️ Action needed: The current CI/build pipeline does not include an ANTLR regeneration step. Add documentation or a build task that regenerates these files when the grammar changes to prevent drift between AssertionRegex.g4 and the generated lexer.

🤖 Prompt for AI Agents
In `@packages/goal-tree/src/antlr/AssertionRegexLexer.ts` around lines 1 - 14, The
generated ANTLR lexer AssertionRegexLexer.ts is not covered by CI regeneration;
add a clear regeneration step and docs: update project CONTRIBUTING or README to
document that packages/lib/grammar/AssertionRegex.g4 is the source and must be
regenerated with ANTLR 4.13.2, add an npm script (e.g., "generate:antlr") that
runs the ANTLR TypeScript generation for AssertionRegex.g4, and add a CI job
that runs this script and fails the build if generated files (like
AssertionRegexLexer.ts) are out of date; reference the generated symbol
AssertionRegexLexer and the grammar file AssertionRegex.g4 in the docs and CI
job to make the relationship explicit.

@@ -0,0 +1 @@
{"fileNames":["../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.array.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.object.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.string.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es2022.full.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/inputstream.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/charstream.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/filestream.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/charstreams.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tokensource.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/token.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/misc/interval.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/misc/intervalset.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/misc/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tokenstream.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/tree.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/syntaxtree.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/parsetree.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/rulenode.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/terminalnode.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/errornode.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/parsetreelistener.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/parsetreevisitor.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/parsetreewalker.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tree/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/state/atnstate.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/state/decisionstate.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/state/rulestartstate.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/state/rulestopstate.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/state/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/atn.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/atnconfig.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/atnconfigset.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/atndeserializationoptions.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/atndeserializer.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/atnsimulator.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/predictioncontextcache.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/dfa/dfa.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/dfa/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/lexeratnsimulator.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/predictionmode.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/parseratnsimulator.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/atn/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/utils/stringtochararray.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/utils/arraytostring.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/utils/printer.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/utils/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/parser.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/context/rulecontext.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/context/parserrulecontext.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/context/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/recognitionexception.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/noviablealtexception.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/failedpredicateexception.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/inputmismatchexception.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/errorstrategy.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/defaulterrorstrategy.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/bailerrorstrategy.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/errorlistener.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/diagnosticerrorlistener.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/error/index.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/recognizer.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/lexer.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/bufferedtokenstream.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/commontoken.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/commontokenstream.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/tokenstreamrewriter.d.ts","../../node_modules/.pnpm/antlr4@4.13.2/node_modules/antlr4/src/antlr4/index.d.cts","./src/antlr/assertionregexlexer.ts","./src/antlr/assertionregexparser.ts","./src/antlr/assertionregexlistener.ts","./src/parsers/getassertionvariables.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/common.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/array.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/collection.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/date.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/function.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/lang.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/math.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/number.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/object.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/seq.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/string.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/common/util.d.ts","../../node_modules/.pnpm/@types+lodash@4.17.23/node_modules/@types/lodash/index.d.ts","./src/antlr/rtregexlexer.ts","./src/antlr/rtregexparser.ts","./src/antlr/rtregexlistener.ts","./src/types.d.ts","./src/parsers/goalnameparser.ts","./src/utils.ts","./src/creation.ts","./src/dependencyresolver.ts","./src/treevariables.ts","./src/printtree.ts","./src/nodeutils.ts","./src/index.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/compatibility/disposable.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/compatibility/indexable.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/compatibility/iterators.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/compatibility/index.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/globals.typedarray.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/buffer.buffer.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/globals.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/web-globals/abortcontroller.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/web-globals/domexception.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/web-globals/events.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/header.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/readable.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/file.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/fetch.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/formdata.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/connector.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/client.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/errors.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/dispatcher.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-dispatcher.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/global-origin.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool-stats.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/pool.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/handlers.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/balanced-pool.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-interceptor.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-client.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-pool.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/mock-errors.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/proxy-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/env-http-proxy-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-handler.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/retry-agent.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/api.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/interceptors.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/util.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cookies.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/patch.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/websocket.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/eventsource.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/filereader.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/diagnostics-channel.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/content-type.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/cache.d.ts","../../node_modules/.pnpm/undici-types@6.21.0/node_modules/undici-types/index.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/web-globals/fetch.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/web-globals/navigator.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/web-globals/storage.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/assert.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/assert/strict.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/async_hooks.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/buffer.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/child_process.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/cluster.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/console.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/constants.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/crypto.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/dgram.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/diagnostics_channel.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/dns.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/dns/promises.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/domain.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/events.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/fs.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/fs/promises.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/http.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/http2.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/https.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/inspector.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/inspector.generated.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/module.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/net.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/os.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/path.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/perf_hooks.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/process.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/punycode.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/querystring.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/readline.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/readline/promises.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/repl.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/sea.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/sqlite.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/stream.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/stream/promises.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/stream/consumers.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/stream/web.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/string_decoder.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/test.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/timers.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/timers/promises.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/tls.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/trace_events.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/tty.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/url.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/util.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/v8.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/vm.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/wasi.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/worker_threads.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/zlib.d.ts","../../node_modules/.pnpm/@types+node@22.19.7/node_modules/@types/node/index.d.ts"],"fileIdsList":[[131,133,134,135,136,137,138,139,140,141,142,143,161,209,226,227],[131,132,134,135,136,137,138,139,140,141,142,143,161,209,226,227],[132,133,134,135,136,137,138,139,140,141,142,143,161,209,226,227],[131,132,133,135,136,137,138,139,140,141,142,143,161,209,226,227],[131,132,133,134,136,137,138,139,140,141,142,143,161,209,226,227],[131,132,133,134,135,137,138,139,140,141,142,143,161,209,226,227],[131,132,133,134,135,136,138,139,140,141,142,143,161,209,226,227],[131,132,133,134,135,136,137,139,140,141,142,143,161,209,226,227],[131,132,133,134,135,136,137,138,140,141,142,143,161,209,226,227],[131,132,133,134,135,136,137,138,139,141,142,143,161,209,226,227],[131,132,133,134,135,136,137,138,139,140,142,143,161,209,226,227],[131,132,133,134,135,136,137,138,139,140,141,143,161,209,226,227],[131,132,133,134,135,136,137,138,139,140,141,142,161,209,226,227],[161,206,207,209,226,227],[161,208,209,226,227],[209,226,227],[161,209,214,226,227,244],[161,209,210,215,220,226,227,229,241,252],[161,209,210,211,220,226,227,229],[161,209,226,227],[156,157,158,161,209,226,227],[161,209,212,226,227,253],[161,209,213,214,221,226,227,230],[161,209,214,226,227,241,249],[161,209,215,217,220,226,227,229],[161,208,209,216,226,227],[161,209,217,218,226,227],[161,209,219,220,226,227],[161,208,209,220,226,227],[161,209,220,221,222,226,227,241,252],[161,209,220,221,222,226,227,236,241,244],[161,202,209,217,220,223,226,227,229,241,252],[161,209,220,221,223,224,226,227,229,241,249,252],[161,209,223,225,226,227,241,249,252],[159,160,161,162,163,164,165,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258],[161,209,220,226,227],[161,209,226,227,228,252],[161,209,217,220,226,227,229,241],[161,209,226,227,230],[161,209,226,227,231],[161,208,209,226,227,232],[161,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258],[161,209,226,227,234],[161,209,226,227,235],[161,209,220,226,227,236,237],[161,209,226,227,236,238,253,255],[161,209,221,226,227],[161,209,220,226,227,241,242,244],[161,209,226,227,243,244],[161,209,226,227,241,242],[161,209,226,227,244],[161,209,226,227,245],[161,206,209,226,227,241,246],[161,209,220,226,227,247,248],[161,209,226,227,247,248],[161,209,214,226,227,229,241,249],[161,209,226,227,250],[161,209,226,227,229,251],[161,209,223,226,227,235,252],[161,209,214,226,227,253],[161,209,226,227,241,254],[161,209,226,227,228,255],[161,209,226,227,256],[161,202,209,226,227],[161,202,209,220,222,226,227,232,241,244,252,254,255,257],[161,209,226,227,241,258],[72,88,109,161,209,226,227],[88,161,209,226,227],[90,161,209,226,227],[89,92,161,209,226,227],[89,90,91,93,95,98,99,100,161,209,226,227],[65,89,94,95,97,120,161,209,226,227],[69,73,89,94,95,97,99,109,120,161,209,226,227],[73,121,161,209,226,227],[65,161,209,226,227],[64,68,69,161,209,226,227],[69,121,122,161,209,226,227],[107,108,161,209,226,227],[69,83,106,107,119,161,209,226,227],[83,106,161,209,226,227],[96,161,209,226,227],[115,161,209,226,227],[69,106,110,114,161,209,226,227],[110,117,120,161,209,226,227],[110,120,161,209,226,227],[69,106,110,161,209,226,227],[106,110,161,209,226,227],[110,111,112,113,114,115,116,117,118,161,209,226,227],[69,101,110,120,161,209,226,227],[65,69,73,109,120,161,209,226,227],[64,65,66,67,69,72,73,83,88,97,101,105,106,109,119,120,121,122,123,124,125,161,209,226,227],[65,69,101,120,161,209,226,227],[70,71,161,209,226,227],[69,161,209,226,227],[70,161,209,226,227],[69,72,73,83,101,105,109,119,120,161,209,226,227],[119,161,209,226,227],[101,161,209,226,227],[84,85,86,87,161,209,226,227],[65,68,161,209,226,227],[69,72,161,209,226,227],[69,70,124,161,209,226,227],[78,161,209,226,227],[76,77,78,79,80,81,82,161,209,226,227],[75,161,209,226,227],[78,79,109,161,209,226,227],[76,77,78,79,161,209,226,227],[76,80,161,209,226,227],[76,161,209,226,227],[74,161,209,226,227],[69,76,109,161,209,226,227],[102,103,104,161,209,226,227],[161,174,178,209,226,227,252],[161,174,209,226,227,241,252],[161,169,209,226,227],[161,171,174,209,226,227,249,252],[161,209,226,227,229,249],[161,209,226,227,259],[161,169,209,226,227,259],[161,171,174,209,226,227,229,252],[161,166,167,170,173,209,220,226,227,241,252],[161,174,181,209,226,227],[161,166,172,209,226,227],[161,174,195,196,209,226,227],[161,170,174,209,226,227,244,252,259],[161,195,209,226,227,259],[161,168,169,209,226,227,259],[161,174,209,226,227],[161,168,169,170,171,172,173,174,175,176,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,196,197,198,199,200,201,209,226,227],[161,174,189,209,226,227],[161,174,181,182,209,226,227],[161,172,174,182,183,209,226,227],[161,173,209,226,227],[161,166,169,174,209,226,227],[161,174,178,182,183,209,226,227],[161,178,209,226,227],[161,172,174,177,209,226,227,252],[161,166,171,174,181,209,226,227],[161,209,226,227,241],[161,169,174,195,209,226,227,257,259],[126,161,209,226,227],[126,128,161,209,226,227],[126,129,161,209,226,227],[126,145,161,209,226,227],[126,146,161,209,226,227],[130,147,148,149,161,209,226,227],[147,149,161,209,226,227],[147,149,150,151,152,153,154,161,209,221,226,227],[147,161,209,226,227],[126,127,128,129,161,209,226,227],[126,143,144,145,146,147,161,209,226,227],[143,161,209,226,227]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7a3c8b952931daebdfc7a2897c53c0a1c73624593fa070e46bd537e64dcd20a","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"3cbad9a1ba4453443026ed38e4b8be018abb26565fa7c944376463ad9df07c41","impliedFormat":1},{"version":"c230e5c79d451178f5bb0672a4459aaba9fcd8accf5da919e9fc44c28a935410","impliedFormat":99},{"version":"8749a2a14224f40523f4b73b4737306314b9c525613504a10f0f5d8734ff6bb9","impliedFormat":99},{"version":"5d53b138462bc7fb5c4177f3e1b9ddbcbb067bf2e2c30efec5756432f7a02783","impliedFormat":99},{"version":"5a47c8ad69dcfe848bc192833b6b3ed97e84019d913adc0fe096e3282a850c1b","impliedFormat":99},{"version":"d831e9a63887aba1d1519f71f3a5bd4630811b5bf9369b0a92ae716afedb3595","impliedFormat":99},{"version":"77855b9f4c8a27aa7298edc60d434623591eb8978999643ee00d7962a58e627d","impliedFormat":99},{"version":"15ee337c63e1b9f0ae6c716f8956dc099ca5cef086c6d22b300b538dab00c192","impliedFormat":99},{"version":"e54ff04895008d9c81a7e75244bdf7d778b09dc866cae9df91773a05a93f398a","impliedFormat":99},{"version":"3cba4781dc00ca50d27ba86627632e7de9570b7199395263d7c7254ad23590d4","impliedFormat":99},{"version":"f32b3896585c2e56e45c2ae10c5d0d2e780df489f44f4fec95d2fa8d8593cb68","impliedFormat":99},{"version":"b189c5be82d2d744cc847d35ef0a0f66ced85bb08721d74a289125d7a773f2c3","impliedFormat":99},{"version":"5b71f48b0e60fc32214d44a36bcc0b66f166c2f32504fb70603b1cfb58f02ceb","impliedFormat":99},{"version":"2b77a0c2fc1bae52dba065890032fce18aefb0ab1b1aadf5d45bf1e27ff4f196","impliedFormat":99},{"version":"0f540fd888c72708e7a18d8f79dc87c52463825e8bb41e8dbc5df63fbf7142d7","impliedFormat":99},{"version":"830ab13db11b09e04bc580b04e7db6dec4b64414c9e254c5bf0157a0f3a59b1f","impliedFormat":99},{"version":"caba7b77a5dcec826d9f8b1743f5fff83db2ed9c936d4f7ea1780724bb25c022","impliedFormat":99},{"version":"6adf4954094ae0e42a229cca490e14df93b5525fa5a7e89ba3a80faa05bdbaa5","impliedFormat":99},{"version":"0aba336dd272d7eb6637da14126c45685a94f66447fa8cfed6d499491fa72942","impliedFormat":99},{"version":"64a1fd5dd513421ac9012ce408225daec74e46aad8a52935e12b61ad626c1126","impliedFormat":99},{"version":"dfedd18b030c8da49695891224da74606b502cf4329ae91cb5d08a6852c831af","impliedFormat":99},{"version":"ace6accf2f8631dc7283e949ccdfe2e5fd1b42e14fbb9139043825e33511aaba","impliedFormat":99},{"version":"2c9b89e86f0e8675e6285328ae446151f1bced3194c70faca47df97ab9dc1834","impliedFormat":99},{"version":"9bdac837352fba0b13df63dbb588d5619dceeb04b74b494731f3ae69184cb07f","impliedFormat":99},{"version":"01abcd66a71a6c9baf0899c623433d21bd7471ff710fb0ab977b49e204db65a6","impliedFormat":99},{"version":"c53b183f533146cf34a56aa39d1bb4be09d8e3836c35ff4798abcb3fbb38e000","impliedFormat":99},{"version":"ab11557d5dc14f5978361c7be95fc8d111d68b9b357d29b7fac1b8786210b87f","impliedFormat":99},{"version":"129abe961e67dffd1ee00db337701724e11efa8b81e4727f1f1c5e6095c11fdb","impliedFormat":99},{"version":"4cdbc6730030fab3022fa824b2c4243876c5080bd6929634b409c019c9464b98","impliedFormat":99},{"version":"33a77396615217fbee394ecbdf9d40829928c3902653316039b0ade770fd4c81","impliedFormat":99},{"version":"839f1025be2210a4ea8fb355753b1429ac2f9fc013d9da04cd3ed0ad2703bde3","impliedFormat":99},{"version":"8b64e0600b0a66e887dcd7d4c2179a387c9187ad72e8c8c962be990c5cee5405","impliedFormat":99},{"version":"b0137bacfc8422a20aa2ce0378591a563b195f771772aeca851c46683392151f","impliedFormat":99},{"version":"176fda74e7858f87fbf6614b3ad257b70ee3c46f404dd6bea73d62d9181c0d64","impliedFormat":99},{"version":"fe694e8a44f980c4a39ccbc6c9e1a2c75ceab9c0444b3303eff8019f52c484ad","impliedFormat":99},{"version":"05d447824ff8445601944e904c2752cd264f43a52727c801b43b57b4506f6d4b","impliedFormat":99},{"version":"3284687e82c5630887b8430174cd1354a9edb7cad13bc9d122873d94915cb4f0","impliedFormat":99},{"version":"4facb8d4dd5ab10bbddab0200bb86b2d4ee8fc62b598c8d4769a9c525964d52e","impliedFormat":99},{"version":"c9d0c59317c7c254e1477aa58bff92120421a19e28dace4e88cd9a33e2e1643f","impliedFormat":99},{"version":"4798c60ccd1c17ec256c5146bb2499d98cdc9f5d18edc7e8d840fdc32e751f36","impliedFormat":99},{"version":"86cada0c2fc6804a410b31946171baaae488e2fbae23c8ca8014dac3c23f6254","impliedFormat":99},{"version":"4b784d9ecd53bbb1e5ae184a4e29021237becb3eabf0be83ad5b1b3903edf099","impliedFormat":99},{"version":"9d34b24e5c0d6b80af7d5e7e0955b604018acc5398bf72974d7e7d6e8dcdb576","impliedFormat":99},{"version":"12edc11c881b5bb0b80b10a67b153a0f9f915914d44e39d17634c5369e230bef","impliedFormat":99},{"version":"c471abd9a8a7307d0bbf6703719cb7a37cd8ea51a4dcba66cf742136ca49d818","impliedFormat":99},{"version":"12893dde5d869073618bca39648143c519fd887a1900862c27dce2427a43e13b","impliedFormat":99},{"version":"2acd88722f08e94a6750ab39d79a2bcc66b04402d48384e6f09536028cecd0f6","impliedFormat":99},{"version":"13e53b214e0d5e3a7572d7756f5ba993e369d5e3f3cb7ce8ef2de96dfea4f1e9","impliedFormat":99},{"version":"3b1f288eef769a3368e4be1116333c8e15238c1d7676e29641251989e5142a6f","impliedFormat":99},{"version":"e4ad233d16a44ad74f3039b9d429c34913cdd76b9016d9ca13b3a210d341dedf","impliedFormat":99},{"version":"ed99666b773147c4ab2362885b256d8310248f15525969c5c84b75a9c468fbb3","impliedFormat":99},{"version":"957492b589b81f43bfec7981ee6065a917eb4bebc54045429f8338b77b717843","impliedFormat":99},{"version":"db2058ee3aacd09e647a354c5c1410f29b7e3d763aaacee590aa4455af6d8889","impliedFormat":99},{"version":"9367a4aa7ce573fed27f9f41d42570d3754cc9288ee016ddb6f37e54acba2690","impliedFormat":99},{"version":"abf65b5a4d2ad20b45f12343fb2c3f476a999fc8ddb19b38f3e8fdd8cf71c576","impliedFormat":99},{"version":"e7c7ee8a8ad4db07eec954fb6a6892486ce1b6247d94932a54203421f6d8b1d8","impliedFormat":99},{"version":"b22a0412dadcb7ec9d7b76d0fa03d1ea01a6fdb36bc442b68e5db57270230b0e","impliedFormat":99},{"version":"30f7921bc1f992091e561b32bd7976ab1e997a044dfac30a5336dfcd5cf06811","impliedFormat":99},{"version":"720913e3730b396d00f930ea27cdb04c878192d93fd4825e78d64788117d4d57","impliedFormat":99},{"version":"6c79cad9c85ee8869353437d121fb86bacf3bfe78e974f94a3a0e363d9d7eeb2","impliedFormat":99},{"version":"e8c14f7445d243cf0b93d60ce2079b596f6e7028cdcec75c493963230c9ff4d7","impliedFormat":99},{"version":"d5aec5fc0cc443e9e29b558465a6b470570e0828bf160aac4300b7a1e85ff819","impliedFormat":99},{"version":"6d439ca69a8256b37371852884106f3652ef2cc5bdb20de122822dd5bad2db99","impliedFormat":99},{"version":"d9534a835fd3a0614d66605b4e229d28e7ecbcb52d5d23f5716b1fd495046473","impliedFormat":1},{"version":"7ea6b59f9b99c39c127374c4b0e57dc431ba0bdce575f419c734fb9309b97ce0","signature":"41ec67627d116966f3e40ae5bbae12d1fb17dbe92111f7e4b7d5837c07ed0556"},{"version":"9f29b631bd0aa86376468ab8bdb923788d41742ee63d57060a7685204774ceff","signature":"be45c8f3d36606dedc453d7abad0dc08308bf7941186f60e85c57b4af3e36c37"},{"version":"43edae48c48f4862982fc98506c4f914a6c6278b543869ed2f733ea1c6e98601","signature":"08a75d503df8f507f215522dcd03ba24d87f6d87f4adce0e71354233bb56f0db"},{"version":"4d6f4fab62e06b4d76ca7cbedb47b75a91bbbda4dfee8c4decae1c93f7c5c8be","signature":"45cb7d0962e2626e7eb05beec576a9e5f0768fed74429560257e9eaa4331d250"},{"version":"380b919bfa0516118edaf25b99e45f855e7bc3fd75ce4163a1cfe4a666388804","impliedFormat":1},{"version":"0d89e5c4ce6e3096e64504e1fa45a8ddccf488cb5fdc1980ea09db2a451f0b91","impliedFormat":1},{"version":"fcf79300e5257a23ed3bacaa6861d7c645139c6f7ece134d15e6669447e5e6db","impliedFormat":1},{"version":"187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","impliedFormat":1},{"version":"aa2c18a1b5a086bbcaae10a4efba409cc95ba7287d8cf8f2591b53704fea3dea","impliedFormat":1},{"version":"5a0b15210129310cee9fa6af9200714bb4b12af4a04d890e15f34dbea1cf1852","impliedFormat":1},{"version":"0244119dbcbcf34faf3ffdae72dab1e9bc2bc9efc3c477b2240ffa94af3bca56","impliedFormat":1},{"version":"00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","impliedFormat":1},{"version":"a873c50d3e47c21aa09fbe1e2023d9a44efb07cc0cb8c72f418bf301b0771fd3","impliedFormat":1},{"version":"7c14ccd2eaa82619fffc1bfa877eb68a012e9fb723d07ee98db451fadb618906","impliedFormat":1},{"version":"49c36529ee09ea9ce19525af5bb84985ea8e782cb7ee8c493d9e36d027a3d019","impliedFormat":1},{"version":"df996e25faa505f85aeb294d15ebe61b399cf1d1e49959cdfaf2cc0815c203f9","impliedFormat":1},{"version":"4f6a12044ee6f458db11964153830abbc499e73d065c51c329ec97407f4b13dd","impliedFormat":1},{"version":"529fdeeed2cb1e3a04d012d1ecdc42934ee6e7b3d2416dd018bce2dc685983ab","signature":"4647828c84f54e2f9141bd7b9c4ad2185b7e0b9940db21bbbb00f563d266811a"},{"version":"0bff4f5b6ccc4f3bc43d33e9aba699f404894d1703522e2f708754d2cdb029c8","signature":"e7ea04265d06430cca1e907a18cab5b5f5cf9cada31897b4001356505d7b30ca"},{"version":"71e8a8ab6ae3f3b4ae0fd26e3a0fea34b539ba4e61ce7123f3541eb36b229f38","signature":"0627f0c3ba70244c21ee48e5db0b19bedcb32366b9371d708169026fc8a23d00"},"52dfff4452e644b02642178d3b321f66649c45b0f6c35d0e1c800d47ac4f0bce",{"version":"d476d1e2bca401ea624e14225ca00fefcf74e8478a327dd7bcb31544c79d7ae4","signature":"941681a57d452e13f846e30476b557e87d08198be9968dad89eb78704f4fb68e"},{"version":"65a9e0d90b15502aa7acda3c06f9c49abd8e80ca08dfddd3168ea4da2bd892dd","signature":"c80382557b919abaec3c4dc3c8b32bd54abacbe06ddd9fab3547f7195d9348a6"},{"version":"dffbe2c593716a941e3e67cc31c72e103150608377cd0f6f613b274e75822c3e","signature":"53320e11fa516c108837d52adb3712956f1372116c622a04256389ee9694333e"},{"version":"7818cb450377b92ec324261f3719f90695f6784bd0fa07777c309ce3c5f052e7","signature":"7eb7d9a0827abcd30f310cd991969cde793b3ef6008c9c4b189bc0507c9a7aaa"},{"version":"8228235e3b11af43c4bbabfc98d6e3c8e90fb0df841c97c884c17bfb3759d479","signature":"5c99700213d3bc2fae4c1f35850c3cfc308fc4b86eeb3773198725b9d464fd1e"},{"version":"b9fd4b095b262cc527dcec660835997a195a7e9debdfe2559d9d1828ac22183c","signature":"b576b06a7024fcac2a3c38cf249351d5028403fce834df064100711e101d0663"},{"version":"7d9de52778341f1029dea76039c022b1f2d627c00e2a6762176cd50abab8cbd5","signature":"5b71aadab561a6731bfda3022c9da9c31dc60d66784770a356daf5f3aaa8802d"},{"version":"2c93932c9c6c172636e96f87cb42030830e084327a6a679fb4162ad66c156f91","signature":"d97efca79e1cd46866873e9e29089d1f5a4bbbea517bab141e1351208d4c0e71"},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"2cbe0621042e2a68c7cbce5dfed3906a1862a16a7d496010636cdbdb91341c0f","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"bb45cd435da536500f1d9692a9b49d0c570b763ccbf00473248b777f5c1f353b","impliedFormat":1},{"version":"6b4e081d55ac24fc8a4631d5dd77fe249fa25900abd7d046abb87d90e3b45645","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"83e63d6ccf8ec004a3bb6d58b9bb0104f60e002754b1e968024b320730cc5311","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2bc7425ef40526650d6db7e072c1ff4a51101c3ac2cc4b666623b19496a6e27","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"f27524f4bef4b6519c604bdb23bf4465bddcccbf3f003abb901acbd0d7404d99","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"dba28a419aec76ed864ef43e5f577a5c99a010c32e5949fe4e17a4d57c58dd11","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c959a391a75be9789b43c8468f71e3fa06488b4d691d5729dde1416dcd38225b","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"0dba70b3fb0dcd713fda33c2df64fa6751fff6460e536971cee917260fb17882","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"9f663c2f91127ef7024e8ca4b3b4383ff2770e5f826696005de382282794b127","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1}],"root":[[127,130],[144,155]],"options":{"composite":true,"declaration":true,"declarationMap":true,"esModuleInterop":true,"module":1,"noUncheckedIndexedAccess":true,"outDir":"./out","rootDir":"./src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9},"referencedMap":[[132,1],[133,2],[131,3],[134,4],[135,5],[136,6],[137,7],[138,8],[139,9],[140,10],[141,11],[142,12],[143,13],[206,14],[207,14],[208,15],[161,16],[209,17],[210,18],[211,19],[156,20],[159,21],[157,20],[158,20],[212,22],[213,23],[214,24],[215,25],[216,26],[217,27],[218,27],[219,28],[220,29],[221,30],[222,31],[162,20],[160,20],[223,32],[224,33],[225,34],[259,35],[226,36],[227,20],[228,37],[229,38],[230,39],[231,40],[232,41],[233,42],[234,43],[235,44],[236,45],[237,45],[238,46],[239,20],[240,47],[241,48],[243,49],[242,50],[244,51],[245,52],[246,53],[247,54],[248,55],[249,56],[250,57],[251,58],[252,59],[253,60],[254,61],[255,62],[256,63],[163,20],[164,20],[165,20],[203,64],[204,20],[205,20],[257,65],[258,66],[89,67],[90,68],[91,69],[92,20],[93,70],[94,20],[101,71],[98,72],[100,73],[95,20],[99,20],[122,74],[65,20],[67,75],[123,76],[124,77],[109,78],[108,79],[107,80],[96,20],[97,81],[116,82],[115,83],[118,84],[117,85],[114,86],[112,87],[119,88],[113,87],[111,89],[110,90],[66,75],[126,91],[64,20],[121,92],[72,93],[70,94],[71,95],[106,96],[120,97],[84,98],[85,68],[88,99],[86,68],[87,68],[69,100],[68,20],[73,101],[125,102],[79,103],[83,104],[76,105],[80,106],[81,107],[82,108],[77,109],[75,110],[78,111],[74,20],[103,20],[105,112],[104,20],[102,20],[61,20],[62,20],[12,20],[10,20],[11,20],[16,20],[15,20],[2,20],[17,20],[18,20],[19,20],[20,20],[21,20],[22,20],[23,20],[24,20],[3,20],[25,20],[26,20],[4,20],[27,20],[31,20],[28,20],[29,20],[30,20],[32,20],[33,20],[34,20],[5,20],[35,20],[36,20],[37,20],[38,20],[6,20],[42,20],[39,20],[40,20],[41,20],[43,20],[7,20],[44,20],[49,20],[50,20],[45,20],[46,20],[47,20],[48,20],[8,20],[54,20],[51,20],[52,20],[53,20],[55,20],[9,20],[56,20],[63,20],[57,20],[58,20],[60,20],[59,20],[1,20],[14,20],[13,20],[181,113],[191,114],[180,113],[201,115],[172,116],[171,117],[200,118],[194,119],[199,120],[174,121],[188,122],[173,123],[197,124],[169,125],[168,118],[198,126],[170,127],[175,128],[176,20],[179,128],[166,20],[202,129],[192,130],[183,131],[184,132],[186,133],[182,134],[185,135],[195,118],[177,136],[178,137],[187,138],[167,139],[190,130],[189,128],[193,20],[196,140],[127,141],[129,142],[128,143],[144,141],[146,144],[145,145],[150,146],[151,147],[155,148],[154,149],[130,150],[148,151],[153,149],[152,147],[147,152],[149,149]],"latestChangedDtsFile":"./out/index.d.ts","version":"5.9.3"} No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid committing TypeScript build artifacts.

tsconfig.tsbuildinfo is generated output and should be ignored to prevent repo churn and machine-specific paths leaking into version control.

🧹 Suggested cleanup
# .gitignore
+packages/goal-tree/tsconfig.tsbuildinfo
+packages/**/tsconfig.tsbuildinfo

Also remove the committed packages/goal-tree/tsconfig.tsbuildinfo file from the repo.

🤖 Prompt for AI Agents
In `@packages/goal-tree/tsconfig.tsbuildinfo` at line 1, The committed file
packages/goal-tree/tsconfig.tsbuildinfo is a generated TypeScript build artifact
and must be removed from version control and ignored; delete
packages/goal-tree/tsconfig.tsbuildinfo from the repo, add an entry to the
repository .gitignore (e.g., tsconfig.tsbuildinfo or
packages/goal-tree/tsconfig.tsbuildinfo) so it isn’t committed again, and commit
the .gitignore change plus the file deletion (use git rm --cached if already
tracked) to stop machine-specific build info from leaking into the repo.

Comment on lines +10 to +19
"timeout": 10000,
"node-option": [
"loader=ts-node/esm"
],
"ts-node": {
"compilerOptions": {
"strict": false,
"noImplicitAny": false
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the full .mocharc.json file to see if ts-node/register is actually being used
cat packages/lib/.mocharc.json

Repository: vieirin/goal-controller

Length of output: 422


🏁 Script executed:

#!/bin/bash
# Check package.json for mocha, ts-node versions and module type
cd packages/lib && cat package.json | jq -r '.dependencies, .devDependencies, .type'

Repository: vieirin/goal-controller

Length of output: 1023


🏁 Script executed:

#!/bin/bash
# Also check root package.json for versions and type
cat package.json | jq -r '.dependencies, .devDependencies, .type' | head -50

Repository: vieirin/goal-controller

Length of output: 157


Remove the ESM loader configuration; it conflicts with ts-node/register.

The .mocharc.json mixes "require": ["ts-node/register"] (CommonJS) with "node-option": ["loader=ts-node/esm"] (ESM). Since package.json does not specify "type": "module", the project is CommonJS-based, and the ESM loader is inappropriate. Keep ts-node/register and remove the "node-option" and "ts-node" entries, or if ESM support is intended, set "type": "module" in package.json and remove ts-node/register.

🤖 Prompt for AI Agents
In `@packages/lib/.mocharc.json` around lines 10 - 19, The .mocharc.json currently
mixes CommonJS and ESM configs: remove the "node-option": ["loader=ts-node/esm"]
and the "ts-node" section so the file relies on the existing "require":
["ts-node/register"] (CommonJS), or if you intend to move to ESM instead, remove
"require": ["ts-node/register"] and add "type": "module" to package.json and
keep the ESM loader; update whichever path you choose so the config is
consistent (reference: "node-option", "ts-node" entries in .mocharc.json and
"require": ["ts-node/register"] and "type": "module" in package.json).

"build": "rm -rf out tsconfig.tsbuildinfo && tsc",
"watch": "tsc --watch",
"test": "mocha",
"test": "TS_NODE_COMPILER_OPTIONS='{\"strict\":false,\"noImplicitAny\":false}' mocha",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Disabling strict type checking in tests masks potential issues.

Setting "strict":false,"noImplicitAny":false in test compilation options defeats the purpose of adding explicit type annotations elsewhere in this PR. This approach hides type errors rather than fixing them, which can lead to runtime issues being caught late.

Consider fixing the underlying type issues in the test files instead of loosening the compiler options.

🤖 Prompt for AI Agents
In `@packages/lib/package.json` at line 14, The test script currently disables
strict type checking by setting TS_NODE_COMPILER_OPTIONS with "strict":false and
"noImplicitAny":false; remove these overrides from the "test" npm script in
package.json and instead fix the failing test typings: run the test suite with
the default/strict TS settings, then update test files (correct any incorrect
types, add explicit annotations, fix missing imports or casts) so that functions
and test helpers referenced in tests (e.g., any custom helpers or mocked
functions in the test files) satisfy TypeScript's strict and noImplicitAny
rules; once tests compile under strict mode, restore the standard test script
without TS_NODE_COMPILER_OPTIONS.

Comment on lines +1 to +8
{
"extends": "../tsconfig.json",
"compilerOptions": {
"strict": false,
"noImplicitAny": false
},
"include": ["./**/*"],
"exclude": []
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for references to the test tsconfig and ts-node configuration
echo "=== Searching for TS_NODE_PROJECT and test/tsconfig.json references ==="
rg "TS_NODE_PROJECT|ts-node\.project|test/tsconfig\.json" --max-count 20

echo ""
echo "=== Check for package.json test script ==="
if [ -f "package.json" ]; then
  cat package.json | head -50
fi

echo ""
echo "=== Look for mocha config files ==="
fd -e mocharc.json -e mocharc.cjs -o .mocharc.json -o .mocharc.cjs

Repository: vieirin/goal-controller

Length of output: 1924


🏁 Script executed:

#!/bin/bash
echo "=== packages/lib/package.json test script ==="
if [ -f "packages/lib/package.json" ]; then
  cat packages/lib/package.json | grep -A 30 '"scripts"'
fi

echo ""
echo "=== Look for mocha config files in packages/lib ==="
find packages/lib -maxdepth 2 -name ".mocharc.*" -o -name "mocha.opts" -o -name ".mocharc.json" -o -name ".mocharc.cjs" -o -name ".mocharc.js" | head -20

echo ""
echo "=== Search for ts-node or typescript test config ==="
rg "ts-node|ts-loader|tsx" packages/lib --max-count 20 -A 3

echo ""
echo "=== Check if test/tsconfig.json is referenced in packages/lib ==="
rg "test/tsconfig" packages/lib/ --max-count 20
</parameter>

Repository: vieirin/goal-controller

Length of output: 1676


🏁 Script executed:

#!/bin/bash
echo "=== Content of packages/lib/.mocharc.json ==="
cat packages/lib/.mocharc.json

echo ""
echo "=== Verify test/tsconfig.json exists and content ==="
cat packages/lib/test/tsconfig.json

Repository: vieirin/goal-controller

Length of output: 675


Remove or wire the test tsconfig—it's currently unused dead code.

The test configuration is applied via .mocharc.json's ts-node.compilerOptions and the TS_NODE_COMPILER_OPTIONS environment variable in package.json, not through test/tsconfig.json. Since test/tsconfig.json is never referenced (no TS_NODE_PROJECT in mocha config), it duplicates settings already defined elsewhere and should either be removed or integrated by having .mocharc.json reference it via TS_NODE_PROJECT instead.

🤖 Prompt for AI Agents
In `@packages/lib/test/tsconfig.json` around lines 1 - 8, test/tsconfig.json is
dead/unused duplicate config; either delete this file or wire it into the
Mocha/ts-node setup by referencing it from .mocharc.json via TS_NODE_PROJECT (or
remove TS_NODE_COMPILER_OPTIONS from package.json and centralize options).
Locate the test/tsconfig.json and .mocharc.json and choose one: if keeping the
tsconfig, update .mocharc.json to set "ts-node": { "project":
"test/tsconfig.json" } (or set TS_NODE_PROJECT accordingly in package.json);
otherwise remove test/tsconfig.json and ensure tests rely only on the existing
TS_NODE_COMPILER_OPTIONS settings already used by .mocharc.json and
package.json.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/lib/src/templateEngine/modules/goalModule/template/pursue/index.ts`:
- Around line 283-292: The access to execCondition.assertion.sentence in the
activationContextCondition computation can throw if execCondition exists but
assertion is missing; update the expression that builds
activationContextCondition (which references isItself(child) and
child.properties.edge.execCondition.assertion.sentence) to use optional chaining
(e.g., child.properties.edge.execCondition?.assertion?.sentence) so it safely
falls back to '' when assertion or sentence is absent; leave the
maintainContextGuard logic as-is since it already uses optional chaining.
- Around line 306-309: The activation-context log call is passing the
state-update variable maintainContextGuard instead of the actual activation
context sentence; change the argument to the activation context (the sentence
derived from the edge, e.g., the value tied to
child.properties.edge.execCondition / the activation context variable created
earlier) when calling pursueLogger.executionDetail.activationContext so logs
show the activation context text rather than the state update. Locate the block
with child.properties.edge.execCondition, logger.trace(child.id, ...) and
replace the maintainContextGuard argument to
pursueLogger.executionDetail.activationContext(...) with the activation context
sentence variable used in that scope.
🧹 Nitpick comments (3)
.gitignore (1)

41-42: Consider removing duplicate ignore entries.

Lines 41-42 duplicate entries already present at lines 7-8. While this doesn't cause functional issues, consolidating these would improve maintainability.

♻️ Suggested cleanup
-packages/ui/.next/
-packages/lib/out/
 packages/lib/tsconfig.tsbuildinfo
 packages/goal-tree/tsconfig.tsbuildinfo
packages/lib/src/prismValidator/expectedElements.ts (1)

91-96: Consider extracting the variable type annotation.

The inline type annotation (v: { name: string }) is repeated in multiple places (lines 93, 121, 128, 175, 182). Consider defining a type alias to improve maintainability.

♻️ Suggested refactor

Define a type alias at the top of the file or import it from the goal-tree package:

type AssertionVariable = { name: string };

Then use it in the forEach callbacks:

-      (v: { name: string }) => {
+      (v: AssertionVariable) => {
packages/lib/src/templateEngine/modules/system/system.ts (1)

118-133: Duplication with expectedElements.ts — consider extracting shared logic.

The task context variable extraction logic (lines 118-133) is nearly identical to the code in packages/lib/src/prismValidator/expectedElements.ts (lines 172-186). Both iterate over tasks, check for assertion and maintain variables, and collect variable names.

This could be extracted into a shared utility function in the goal-tree package or a common lib utility to reduce duplication.

♻️ Potential shared utility
// Could be added to goal-tree or a shared utility file
export const collectTaskContextVariables = (tasks: GoalNode[]): Set<string> => {
  const taskContextVariables = new Set<string>();
  tasks.forEach((task: GoalNode) => {
    if (task.properties.edge.execCondition?.assertion) {
      task.properties.edge.execCondition.assertion.variables.forEach(
        (v: { name: string }) => taskContextVariables.add(v.name)
      );
    }
    if (task.properties.edge.execCondition?.maintain?.variables) {
      task.properties.edge.execCondition.maintain.variables.forEach(
        (v: { name: string }) => taskContextVariables.add(v.name)
      );
    }
  });
  return taskContextVariables;
};

…nsistency

- Move maxRetries from GoalNode.properties to GoalNode.properties.edge
- Both GoalNode and Task now have maxRetries in properties.edge
- Add isGoalNode and isTask type guards to nodeUtils.ts
- Export Task and TreeNode types from index.ts
- Update childrenWithMaxRetries to check properties.edge.maxRetries
- Split types.ts into separate files under types/ directory
- Update logger.ts to use Task and Resource types
- Update expectedElements.ts with proper Task/Resource types
- Update sleecTemplateEngine files to use Task type
- Update changeManager template files to use Task type
- Update goalModule template files with PursueableNode type
- Update pursue/common.ts to accept GoalNode | Task
- Update andGoal.ts with AchievableNode type
- Update orGoal.ts to filter resources from pursueable children
- Update achieve.ts to filter resources when building conditions
- Update skip.ts to use TreeNode for children
- Update system.ts to use Task and Resource types
- Add maxRetries to GoalNode.properties.edge
- Export Task, TreeNode, isGoalNode, isTask from goal-tree
- Update creation.ts with proper type handling
- Update utils.ts for TreeNode handling
- Fix all type errors in goal-tree and lib packages
- Move PreCond, TriggeringEvent, TemporalConstraint, PostCond, ObstacleEvent to Task.properties.sleec
- Move utility and cost to GoalEdgeProps
- Remove resource-specific properties from GoalNode
- Remove index signature from properties types
- Create TaskEdgeProps and GoalEdgeProps types
- Create TaskSleecProps type for task SLEEC properties
- Rename sleecProps to sleec in GoalNode.properties
- Update creation.ts and lib usages accordingly
- Move dependsOn from GoalNode to properties.edge.dependsOn
- Change dependsOn type from string[] to GoalNode[]
- Add pendingDependencies map for resolving dependencies during tree creation
- Remove variantOf property and related code from GoalNode
- Remove non-existent dependencyResolver exports from index.ts
- Update pursue/index.ts and tests to use new property path
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/lib/src/templateEngine/modules/goalModule/template/achieve.ts (1)

17-29: Avoid returning () when all children are resources.
If children contains only resources, the filter yields an empty list and the function returns (). That can produce an invalid condition string. Consider checking the filtered list before formatting.

🔧 Proposed fix
   if (isValidSeparator(goal.relationToChildren)) {
     const children = childrenIncludingTasks({ node: goal });
-    if (children.length) {
-      return `(${children
-        .filter(
-          (child): child is Exclude<TreeNode, { type: 'resource' }> =>
-            !isResource(child),
-        )
-        .map((child) =>
-          child.properties.edge.execCondition?.maintain
-            ? `${achievedMaintain(child.id)}=true`
-            : `${achievedVariable(child.id)}=1`,
-        )
-        .join(separator(goal.relationToChildren))})`;
-    }
+    const pursueableChildren = children.filter(
+      (child): child is Exclude<TreeNode, { type: 'resource' }> =>
+        !isResource(child),
+    );
+    if (pursueableChildren.length) {
+      return `(${pursueableChildren
+        .map((child) =>
+          child.properties.edge.execCondition?.maintain
+            ? `${achievedMaintain(child.id)}=true`
+            : `${achievedVariable(child.id)}=1`,
+        )
+        .join(separator(goal.relationToChildren))})`;
+    }
   }
packages/lib/src/sleecTemplateEngine/rules.ts (1)

4-34: Guard against missing sleec properties to prevent "undefined" in rules.

The optional chaining operator injects undefined into template strings when properties are missing. Since task.properties.sleec is optional (TaskSleecProps), tasks reaching generateTaskRules() without SLEEC data will generate invalid rules like:

Rule[id]_1 when undefined and undefined then ...

This occurs because there is no upstream filtering—allByType() returns all tasks regardless of SLEEC presence. Add a check at the start of the .map() callback to either skip tasks without SLEEC or throw an error, similar to the validation pattern in purposes.ts.

🛠️ Suggested guard
 export const generateTaskRules = (tasks: Task[]): string => {
   const hasObstacleEvents = (task: Task) =>
     !!task.properties.sleec?.ObstacleEvent;
   return `rule_start
       ${tasks
         .map((task) => {
+          const sleec = task.properties.sleec;
+          if (!sleec) {
+            throw new Error(`Task "${task.name}" (${task.id}) lacks required SLEEC properties`);
+          }
           return `
-      Rule${renameTaskId(task.id)}_1 when ${
-        task.properties.sleec?.TriggeringEvent
-      } and ${task.properties.sleec?.PreCond} then ${taskFluentName(task, 'Start')}
+      Rule${renameTaskId(task.id)}_1 when ${
+        sleec.TriggeringEvent
+      } and ${sleec.PreCond} then ${taskFluentName(task, 'Start')}
       Rule${renameTaskId(task.id)}_2 when ${taskFluentName(
         task,
         'Start',
       )} then ${taskFluentName(task, 'Pursuing')} within ${
-        task.properties.sleec?.TemporalConstraint
+        sleec.TemporalConstraint
       }
       Rule${renameTaskId(task.id)}_3 when ${taskFluentName(
         task,
         'Pursuing',
-      )} and ${task.properties.sleec?.PostCond} then ${taskFluentName(
+      )} and ${sleec.PostCond} then ${taskFluentName(
         task,
         'Achieved',
-      )} unless (not ${task.properties.sleec?.PostCond}) then ${taskFluentName(
+      )} unless (not ${sleec.PostCond}) then ${taskFluentName(
         task,
         'ReportFailure',
       )} 
       ${hasObstacleEvents(task) ? generateObstacleEventsRules(task) : ''}`;
         })
         .join('\n')}
🤖 Fix all issues with AI agents
In `@packages/goal-tree/src/creation.ts`:
- Around line 529-537: The code in resolvedDependencies maps
node.properties.edge.dependsOn and unconditionally casts depNode from nodeMap to
GoalNode, which is unsafe because depNode may be a Task or Resource; update the
mapping to validate the runtime type before casting: use a type guard or
discriminator on depNode (e.g., check depNode.type or instanceof checks) to
accept only GoalNode when building a goal's resolvedDependencies, throw a clear
error if the referenced id is not a GoalNode, and adjust any downstream
consumers if they must handle TaskNode/ResourceNode instead; ensure you
reference the resolvedDependencies variable, the nodeMap lookup, the dependsOn
property, and GoalNode type in your changes.

In `@packages/goal-tree/src/types/istar.ts`:
- Around line 15-41: The CustomProperties type currently intersects a `{ type:
never }` with `{ type: 'maintain'; maintain: string; assertion: string }`,
making `type` impossible to satisfy; change it to a proper discriminated union:
define the shared/common structure (the customProperties object keys like
Description, cost, etc.) as a base type without `type: never`, then create a
union of variants (e.g., one variant with `type: 'maintain'` that includes
`maintain` and `assertion` and another variant for the default/other types that
omits those fields or uses optional fields). Update `CustomProperties` to be
that union so the runtime check in creation.ts (`if (customProperties.type ===
'maintain')`) typesafe-ly narrows to the variant exposing `maintain` and
`assertion`.

In `@packages/lib/src/logger/logger.ts`:
- Around line 216-218: The log line calls .join() on the result of
goal.tasks?.map(...), which can be undefined and throws; change the expression
so .join() always runs on an array—e.g., use a safe fallback like (goal.tasks ??
[]).map((task: Task) => task.id).join(', ') or a conditional check goal.tasks &&
goal.tasks.length ? goal.tasks.map((task: Task) => task.id).join(', ') : 'none'
in the write call to ensure goals without tasks do not cause an exception; refer
to the existing write call and the Task type to implement the fix.

In `@packages/lib/src/sleecTemplateEngine/definitions.ts`:
- Around line 158-163: The extractObstacleEvents function is producing entries
like "Achievedundefined" because it prefixes "Achieved" before filtering out
missing sleec.ObstacleEvent values; update extractObstacleEvents to only
map/format after verifying task.properties.sleec?.ObstacleEvent is present
(e.g., filter tasks with a defined ObstacleEvent first or use a flatMap that
returns [] for missing values) so the Set is built from actual event strings;
key symbols: extractObstacleEvents, Task, and
task.properties.sleec?.ObstacleEvent.

In `@packages/lib/src/templateEngine/modules/goalModule/template/pursue/index.ts`:
- Around line 29-35: The dependency guard in goalDependencyStatement currently
reads legacy goal.dependsOn; update it to read the new path
(goal.properties.edge.dependsOn) and guard for existence/length there, and when
mapping use the resolved GoalNode objects (or resolve IDs to GoalNode if
dependsOn now contains IDs) before calling hasBeenAchieved; keep using
separator('and') and ensure the function still returns an empty string when no
dependencies exist.

In `@packages/lib/test/sleecTemplateEngine/purposes.test.ts`:
- Around line 3-26: The test helper createGoalNode is missing required GoalNode
properties used at runtime (properties.edge and properties.isQuality); update
createGoalNode to populate properties.edge with a sensible minimal default
(e.g., null or an empty object consistent with GoalNode.edge shape) and
properties.isQuality with a boolean default (false) so fixtures match the
runtime shape expected by generatePurposes and other GoalTree consumers; ensure
the defaults are set on the returned object's properties field alongside sleec
to avoid undefined access in tests.
🧹 Nitpick comments (7)
packages/lib/src/templateEngine/modules/goalModule/template/skip.ts (1)

1-1: Unused Task import.

The Task type is imported but not used anywhere in this file. Consider removing it.

-import type { GoalNode, Task, TreeNode } from '@goal-controller/goal-tree';
+import type { GoalNode, TreeNode } from '@goal-controller/goal-tree';
packages/lib/src/templateEngine/modules/system/system.ts (1)

117-132: Remove redundant type annotations where TypeScript can infer them.

The explicit type annotations are unnecessary because:

  • allByType({ gm, type: 'task' }) already narrows the return type to Task[] through function overloads
  • ExecCondition properties already define variables as Array<{ name: string; value: boolean | null }>

This pattern is already used elsewhere in the codebase (see treeVariables.ts lines 11-27) without explicit annotations. Simplify to:

-  tasks.forEach((task: Task) => {
+  tasks.forEach((task) => {
     if (task.properties.edge.execCondition?.assertion) {
       task.properties.edge.execCondition.assertion.variables.forEach(
-        (v: { name: string }) => {
+        (v) => {
           taskContextVariables.add(v.name);
         },
       );
     }
     if (task.properties.edge.execCondition?.maintain?.variables) {
       task.properties.edge.execCondition.maintain.variables.forEach(
-        (v: { name: string }) => {
+        (v) => {
           taskContextVariables.add(v.name);
         },
       );
     }
   });
packages/lib/src/templateEngine/modules/changeManager/template/tasks/variables.ts (1)

1-34: Guard against undefined maxRetries when building ranges.

If a task lacks edge.maxRetries, the current output can become [0..undefined]. A defensive default keeps the generated PRISM valid.

♻️ Proposed hardening
-export const maxRetriesVariable = (task: Task): string => {
-  const maxRetries = task.properties.edge.maxRetries;
-  if (maxRetries === 0) {
+export const maxRetriesVariable = (task: Task): string => {
+  const maxRetries = task.properties.edge.maxRetries ?? 0;
+  if (maxRetries <= 0) {
     return '';
   }
packages/goal-tree/src/creation.ts (4)

73-97: Potential empty string in decision variable name.

When d[0] is undefined, the fallback ?? '' will produce an empty variable name. This could silently create invalid decision entries.

Consider adding validation:

🔧 Suggested improvement
   return parsedDecision.map((d) => ({
-    variable: d[0]?.trim() ?? '',
+    variable: d[0]?.trim() || (() => { throw new Error(`[INVALID DECISION] variable name is empty in: ${decision}`); })(),
     space: parseInt(d[1] ?? ''),
   }));

178-179: Consider specifying radix for parseInt calls.

Multiple parseInt calls lack an explicit radix parameter. While modern JavaScript defaults to base 10 for most inputs, specifying radix 10 explicitly is a best practice for clarity and to avoid edge cases with unusual input formats.

♻️ Suggested fix
-      const lowerBoundInt = parseInt(lowerBound);
-      const upperBoundInt = parseInt(upperBound);
+      const lowerBoundInt = parseInt(lowerBound, 10);
+      const upperBoundInt = parseInt(upperBound, 10);
-      const initialValueInt = parseInt(initialValue);
+      const initialValueInt = parseInt(initialValue, 10);
-          maxRetries: maxRetries ? parseInt(maxRetries) : 0,
+          maxRetries: maxRetries ? parseInt(maxRetries, 10) : 0,

Also applies to: 193-193, 364-364, 396-396


305-306: Variable shadowing: sleec is redeclared inside the task block.

The outer sleec (line 305-306, from extractSleecProps) is computed but unused when nodeType === 'task'. Inside the task block (line 327), a new sleec variable with a different structure shadows it. This works but is confusing.

Consider renaming one of the variables for clarity:

♻️ Suggested refactor
   // Extract SLEEC properties if present
-  const sleec = extractSleecProps(customProperties);
+  const goalSleec = extractSleecProps(customProperties);

   if (nodeType === 'resource') {
     // ...unchanged
   }

   if (nodeType === 'task') {
-    const sleec =
+    const taskSleec =
       customProperties.PreCond ||
       // ... rest unchanged
-          }
-        : undefined;
+          }
+        : undefined;

     const taskNode: Task = {
       // ...
       properties: {
         edge: { /* ... */ },
-        ...(sleec && { sleec }),
+        ...(taskSleec && { sleec: taskSleec }),
       },
     };
     return taskNode;
   }

   if (nodeType === 'goal') {
     const goalNode: GoalNode = {
       // ...
       properties: {
         // ...
-        ...(sleec && { sleec }),
+        ...(goalSleec && { sleec: goalSleec }),
       },
       // ...
     };
     return goalNode;
   }

Also applies to: 326-350


540-542: Type assertion as GoalNode[] | undefined may mask type errors.

The resolveNodeDependencies recursively processes children, but the assertion assumes all children are GoalNode. If node.children contains other types in the future, this could silently produce incorrect types.

Consider using a type guard or validating the result:

♻️ Alternative approach
-    const resolvedChildren = node.children?.map(resolveNodeDependencies) as
-      | GoalNode[]
-      | undefined;
+    const resolvedChildren = node.children?.map((child) => {
+      const resolved = resolveNodeDependencies(child);
+      if (resolved.type !== 'goal') {
+        throw new Error(`[INVALID MODEL]: Child ${resolved.id} is not a goal`);
+      }
+      return resolved;
+    });

Comment on lines 529 to 537
const resolvedDependencies = node.properties.edge.dependsOn.map((depId) => {
const depNode = nodeMap.get(depId);
if (!depNode) {
throw new Error(
`[INVALID MODEL]: Dependency ${depId} not found for node ${node.id}`,
);
}
return depNode;
return depNode as GoalNode;
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unsafe type assertion: dependency could be a Task or Resource.

The nodeMap contains all node types (goals, tasks, resources), but line 536 casts depNode to GoalNode without validation. If a goal's dependsOn references a task or resource ID, this would produce an incorrect type at runtime.

Consider adding a type guard:

🔧 Suggested fix
     const resolvedDependencies = node.properties.edge.dependsOn.map((depId) => {
       const depNode = nodeMap.get(depId);
       if (!depNode) {
         throw new Error(
           `[INVALID MODEL]: Dependency ${depId} not found for node ${node.id}`,
         );
       }
-      return depNode as GoalNode;
+      if (depNode.type !== 'goal') {
+        throw new Error(
+          `[INVALID MODEL]: Dependency ${depId} for node ${node.id} must be a goal, but found ${depNode.type}`,
+        );
+      }
+      return depNode;
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const resolvedDependencies = node.properties.edge.dependsOn.map((depId) => {
const depNode = nodeMap.get(depId);
if (!depNode) {
throw new Error(
`[INVALID MODEL]: Dependency ${depId} not found for node ${node.id}`,
);
}
return depNode;
return depNode as GoalNode;
});
const resolvedDependencies = node.properties.edge.dependsOn.map((depId) => {
const depNode = nodeMap.get(depId);
if (!depNode) {
throw new Error(
`[INVALID MODEL]: Dependency ${depId} not found for node ${node.id}`,
);
}
if (depNode.type !== 'goal') {
throw new Error(
`[INVALID MODEL]: Dependency ${depId} for node ${node.id} must be a goal, but found ${depNode.type}`,
);
}
return depNode;
});
🤖 Prompt for AI Agents
In `@packages/goal-tree/src/creation.ts` around lines 529 - 537, The code in
resolvedDependencies maps node.properties.edge.dependsOn and unconditionally
casts depNode from nodeMap to GoalNode, which is unsafe because depNode may be a
Task or Resource; update the mapping to validate the runtime type before
casting: use a type guard or discriminator on depNode (e.g., check depNode.type
or instanceof checks) to accept only GoalNode when building a goal's
resolvedDependencies, throw a clear error if the referenced id is not a
GoalNode, and adjust any downstream consumers if they must handle
TaskNode/ResourceNode instead; ensure you reference the resolvedDependencies
variable, the nodeMap lookup, the dependsOn property, and GoalNode type in your
changes.

Comment on lines +15 to +41
export type CustomProperties = {
customProperties: {
Description: '';
root?: string;
cost: string;
alt?: string;
utility: string;
dependsOn?: string;
variables?: string;
uniqueChoice?: string;
type: never;
maxRetries?: string;
selected?: string;
PreCond?: string;
TriggeringEvent?: string;
TemporalConstraint?: string;
PostCond?: string;
ObstacleEvent?: string;
// Resource-specific properties
initialValue?: string;
lowerBound?: string;
upperBound?: string;
} & {
type: 'maintain';
maintain: string;
assertion: string;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/goal-tree/src/types/istar.ts | sed -n '1,50p'

Repository: vieirin/goal-controller

Length of output: 1426


🏁 Script executed:

rg "CustomProperties|customProperties" packages/goal-tree/src --type ts --type tsx -A 2 -B 2 | head -100

Repository: vieirin/goal-controller

Length of output: 94


🏁 Script executed:

rg "CustomProperties|customProperties" packages/goal-tree/src --type ts -A 2 -B 2 | head -100

Repository: vieirin/goal-controller

Length of output: 5748


🏁 Script executed:

# Check if there are other type definitions in istar.ts that might extend CustomProperties
cat -n packages/goal-tree/src/types/istar.ts | sed -n '40,120p'

Repository: vieirin/goal-controller

Length of output: 1248


🏁 Script executed:

# Check if there's a tsconfig.json to see type checking configuration
cat -n packages/goal-tree/tsconfig.json 2>/dev/null || echo "No tsconfig in goal-tree"

Repository: vieirin/goal-controller

Length of output: 436


🏁 Script executed:

# Let's check how Node, Actor, and Diagram types (which use CustomProperties) are actually instantiated/used
rg "new Node|new Actor|new Diagram|: Node|: Actor|: Diagram" packages/goal-tree/src --type ts -B 2 -A 2 | head -80

Repository: vieirin/goal-controller

Length of output: 2219


🏁 Script executed:

# Let me verify the actual type behavior by checking if there are any compile errors
# Look for the build output or check if tests pass
ls -la packages/goal-tree/

Repository: vieirin/goal-controller

Length of output: 403


Fix CustomProperties type intersection that makes type property impossible to satisfy.

The intersection { type: never } & { type: 'maintain' } is logically contradictory and creates an uninhabitable type. The code in creation.ts checks if (customProperties.type === 'maintain') and accesses customProperties.maintain and customProperties.assertion, but the type definition declares these properties can never exist simultaneously. Use a union-based approach to allow conditional properties:

🛠️ Proposed fix
+type MaintainCustomProperties =
+  | {
+      type: 'maintain';
+      maintain: string;
+      assertion: string;
+    }
+  | {
+      type?: never;
+      maintain?: never;
+      assertion?: never;
+    };
+
 export type CustomProperties = {
   customProperties: {
     Description: '';
     root?: string;
     cost: string;
     alt?: string;
     utility: string;
     dependsOn?: string;
     variables?: string;
     uniqueChoice?: string;
-    type: never;
     maxRetries?: string;
     selected?: string;
     PreCond?: string;
     TriggeringEvent?: string;
     TemporalConstraint?: string;
     PostCond?: string;
     ObstacleEvent?: string;
     // Resource-specific properties
     initialValue?: string;
     lowerBound?: string;
     upperBound?: string;
-  } & {
-    type: 'maintain';
-    maintain: string;
-    assertion: string;
-  };
+  } & MaintainCustomProperties;
 };
🤖 Prompt for AI Agents
In `@packages/goal-tree/src/types/istar.ts` around lines 15 - 41, The
CustomProperties type currently intersects a `{ type: never }` with `{ type:
'maintain'; maintain: string; assertion: string }`, making `type` impossible to
satisfy; change it to a proper discriminated union: define the shared/common
structure (the customProperties object keys like Description, cost, etc.) as a
base type without `type: never`, then create a union of variants (e.g., one
variant with `type: 'maintain'` that includes `maintain` and `assertion` and
another variant for the default/other types that omits those fields or uses
optional fields). Update `CustomProperties` to be that union so the runtime
check in creation.ts (`if (customProperties.type === 'maintain')`) typesafe-ly
narrows to the variant exposing `maintain` and `assertion`.

Comment on lines 216 to 218
write(
`\tTasks: ${goal.tasks?.map((task) => task.id).join(', ') ?? 'none'}\n`,
`\tTasks: ${goal.tasks?.map((task: Task) => task.id).join(', ') ?? 'none'}\n`,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against undefined tasks before calling .join()

goal.tasks?.map(...).join(', ') still calls .join() on undefined when tasks is missing, which throws and can abort logging for goals without tasks.

🛠️ Proposed fix
-      write(
-        `\tTasks: ${goal.tasks?.map((task: Task) => task.id).join(', ') ?? 'none'}\n`,
-      );
+      write(
+        `\tTasks: ${
+          goal.tasks && goal.tasks.length > 0
+            ? goal.tasks.map((task: Task) => task.id).join(', ')
+            : 'none'
+        }\n`,
+      );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
write(
`\tTasks: ${goal.tasks?.map((task) => task.id).join(', ') ?? 'none'}\n`,
`\tTasks: ${goal.tasks?.map((task: Task) => task.id).join(', ') ?? 'none'}\n`,
);
write(
`\tTasks: ${
goal.tasks && goal.tasks.length > 0
? goal.tasks.map((task: Task) => task.id).join(', ')
: 'none'
}\n`,
);
🤖 Prompt for AI Agents
In `@packages/lib/src/logger/logger.ts` around lines 216 - 218, The log line calls
.join() on the result of goal.tasks?.map(...), which can be undefined and
throws; change the expression so .join() always runs on an array—e.g., use a
safe fallback like (goal.tasks ?? []).map((task: Task) => task.id).join(', ') or
a conditional check goal.tasks && goal.tasks.length ? goal.tasks.map((task:
Task) => task.id).join(', ') : 'none' in the write call to ensure goals without
tasks do not cause an exception; refer to the existing write call and the Task
type to implement the fix.

Comment on lines 158 to 163
const extractObstacleEvents = (tasks: Task[]): Set<string> =>
new Set(
tasks
.map((task) => `Achieved${task.properties.ObstacleEvent}`)
.map((task) => `Achieved${task.properties.sleec?.ObstacleEvent}`)
.filter((event): event is string => !!event),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid emitting Achievedundefined obstacle events.

The current mapping prefixes before checking for an actual ObstacleEvent, so a missing value yields a truthy Achievedundefined entry.

🐛 Proposed fix
-  new Set(
-    tasks
-      .map((task) => `Achieved${task.properties.sleec?.ObstacleEvent}`)
-      .filter((event): event is string => !!event),
-  );
+  new Set(
+    tasks
+      .map((task) => task.properties.sleec?.ObstacleEvent)
+      .filter((event): event is string => !!event)
+      .map((event) => `Achieved${event}`),
+  );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const extractObstacleEvents = (tasks: Task[]): Set<string> =>
new Set(
tasks
.map((task) => `Achieved${task.properties.ObstacleEvent}`)
.map((task) => `Achieved${task.properties.sleec?.ObstacleEvent}`)
.filter((event): event is string => !!event),
);
const extractObstacleEvents = (tasks: Task[]): Set<string> =>
new Set(
tasks
.map((task) => task.properties.sleec?.ObstacleEvent)
.filter((event): event is string => !!event)
.map((event) => `Achieved${event}`),
);
🤖 Prompt for AI Agents
In `@packages/lib/src/sleecTemplateEngine/definitions.ts` around lines 158 - 163,
The extractObstacleEvents function is producing entries like "Achievedundefined"
because it prefixes "Achieved" before filtering out missing sleec.ObstacleEvent
values; update extractObstacleEvents to only map/format after verifying
task.properties.sleec?.ObstacleEvent is present (e.g., filter tasks with a
defined ObstacleEvent first or use a flatMap that returns [] for missing values)
so the Set is built from actual event strings; key symbols:
extractObstacleEvents, Task, and task.properties.sleec?.ObstacleEvent.

Comment on lines 3 to 26
import type {
GoalNode,
GoalTree,
SleecProps,
} from '@goal-controller/goal-tree';
import { generatePurposes } from '../../src/sleecTemplateEngine/purposes';

/**
* Helper to create a minimal GoalNode with sleecProps
* Helper to create a minimal GoalNode with sleec props
*/
const createGoalNode = (
id: string,
name: string,
sleecProps?: SleecProps,
sleec?: SleecProps,
): GoalNode =>
({
id,
name,
type: 'goal',
properties: {},
properties: {
sleec,
},
resources: [],
sleecProps,
}) as unknown as GoalNode;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Populate required GoalNode properties in the test helper.
The helper now omits properties.edge and properties.isQuality, which can cause runtime undefined access if the code under test touches them. Add minimal defaults to keep test fixtures aligned with the runtime shape.

✅ Suggested minimal fixture defaults
 const createGoalNode = (
   id: string,
   name: string,
   sleec?: SleecProps,
 ): GoalNode =>
   ({
     id,
     name,
     type: 'goal',
     properties: {
       sleec,
+      isQuality: false,
+      edge: {},
     },
     resources: [],
   }) as unknown as GoalNode;
🤖 Prompt for AI Agents
In `@packages/lib/test/sleecTemplateEngine/purposes.test.ts` around lines 3 - 26,
The test helper createGoalNode is missing required GoalNode properties used at
runtime (properties.edge and properties.isQuality); update createGoalNode to
populate properties.edge with a sensible minimal default (e.g., null or an empty
object consistent with GoalNode.edge shape) and properties.isQuality with a
boolean default (false) so fixtures match the runtime shape expected by
generatePurposes and other GoalTree consumers; ensure the defaults are set on
the returned object's properties field alongside sleec to avoid undefined access
in tests.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/goal-tree/src/creation.ts (2)

520-575: Ensure pendingDependencies is cleared on all paths (and avoid shared state).

pendingDependencies is module-scoped and only cleared on the happy path. If dependency resolution throws, stale entries can leak into subsequent calls, and concurrent calls can cross-contaminate. Please clear in a finally (or make the map function-scoped).

🧹 Minimal hardening (ensure cleanup on error)
-  const result = tree.map(resolveNodeDependencies);
-
-  // Clear pending dependencies after resolution
-  pendingDependencies.clear();
-
-  return result;
+  try {
+    const result = tree.map(resolveNodeDependencies);
+    return result;
+  } finally {
+    // Clear pending dependencies after resolution (even on error)
+    pendingDependencies.clear();
+  }

173-202: Allow zero-valued bounds/initial values for int resources.

The current falsy checks reject "0" for initialValue, lowerBound, or upperBound, which makes valid resources fail parsing. Check for null/undefined/'' instead.

✅ Proposed fix
-      if (!initialValue || !lowerBound || !upperBound) {
+      if (
+        initialValue == null ||
+        lowerBound == null ||
+        upperBound == null ||
+        initialValue === '' ||
+        lowerBound === '' ||
+        upperBound === ''
+      ) {
         throw new Error(
           `[INVALID RESOURCE]: Resource ${resource.id} must have an initial value, lower bound, and upper bound`,
         );
       }
🧹 Nitpick comments (2)
packages/lib/test/templateEngine/modules/goalModule/template/pursue/goalDependency.test.ts (1)

3-5: Consolidate @goal-controller/goal-tree imports.

Three separate imports from the same package add noise; a single import is clearer.

♻️ Suggested refactor
-import { loadPistarModel } from '@goal-controller/goal-tree';
-import { convertToTree } from '@goal-controller/goal-tree';
-import { allGoalsMap } from '@goal-controller/goal-tree';
+import { loadPistarModel, convertToTree, allGoalsMap } from '@goal-controller/goal-tree';
packages/goal-tree/src/utils.ts (1)

105-109: Fix leafGoals return type (currently unsound).

leafGoals always returns GoalNode[], but its signature returns T. This can mislead callers and weaken type safety.

♻️ Proposed fix
-export const leafGoals = <T extends GenericTree>({ gm }: { gm: T }): T => {
-  const leaves = allByType({ gm, type: 'goal' })?.filter(
+export const leafGoals = ({ gm }: { gm: GenericTree }): GoalNode[] => {
+  const leaves = allByType({ gm, type: 'goal' }).filter(
     (goal) => !!goal.tasks,
   );
-  return leaves as T;
+  return leaves;
 };

- Remove relationToParent from BaseNode
- Remove GoalNodeWithParent, GenericGoal, GenericTreeNode, GoalTreeWithParent, GenericTree types
- Update utils.ts to use TreeNode and GoalTree instead of generic types
- Remove preferVariant parameter from utility functions
- Update nodeUtils.ts to use TreeNode
- Tree is now a simple unidirectional structure
- Restructured goal-tree as an SDK with GoalTree class, Node and Model namespaces
- Moved type properties: sleecProps→properties.sleec, utility/cost→properties.edge
- Made PreCond/TriggeringEvent/TemporalConstraint/PostCond/ObstacleEvent Task-exclusive
- Changed dependsOn from string[] to GoalNode[] with proper resolution
- Removed bidirectional tree references (relationToParent, generic types)
- Moved tests from lib to goal-tree with proper TypeScript config
- Updated all consumers in lib and ui packages
- Applied Prettier formatting across all packages
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/lib/src/templateEngine/modules/goalModule/template/achieve.ts (1)

16-29: Guard against resource-only children returning ().
Right now the length check happens before filtering resources. If a goal’s children are all resources, this produces an empty () condition and can generate invalid output. Filter first, then guard on the filtered length.

🛠️ Proposed fix
-    const children = Node.children(goal);
-    if (children.length) {
-      return `(${children
-        .filter(
-          (child): child is Exclude<TreeNode, { type: 'resource' }> =>
-            !Node.isResource(child),
-        )
-        .map((child) =>
-          child.properties.edge.execCondition?.maintain
-            ? `${achievedMaintain(child.id)}=true`
-            : `${achievedVariable(child.id)}=1`,
-        )
-        .join(separator(goal.relationToChildren))})`;
-    }
+    const children = Node.children(goal).filter(
+      (child): child is Exclude<TreeNode, { type: 'resource' }> =>
+        !Node.isResource(child),
+    );
+    if (children.length) {
+      return `(${children
+        .map((child) =>
+          child.properties.edge.execCondition?.maintain
+            ? `${achievedMaintain(child.id)}=true`
+            : `${achievedVariable(child.id)}=1`,
+        )
+        .join(separator(goal.relationToChildren))})`;
+    }
packages/lib/src/mdp/common.ts (1)

36-37: Pre-existing bug: AND function uses wrong separator.

The AND function incorrectly uses separator('or') instead of separator('and'). While this appears to be a pre-existing bug and not introduced by this PR, it should be fixed.

🐛 Proposed fix
 export const AND = (elements: string[]): string =>
-  elements.join(separator('or'));
+  elements.join(separator('and'));
🤖 Fix all issues with AI agents
In `@packages/goal-tree/src/antlr/RTRegexLexer.ts`:
- Around line 1-14: The generated ANTLR files in packages/goal-tree/src/antlr
(e.g., RTRegexLexer.ts) are duplicated while the grammar source RTRegex.g4 lives
only in packages/lib/grammar; fix by either (A) removing the generated files
from goal-tree and making goal-tree depend on lib’s ANTLR artifacts (export the
generated JS/TS from packages/lib and import them in goal-tree, updating
package.json to add that dependency and removing RTRegexLexer.ts and sibling
files), or (B) copy packages/lib/grammar/RTRegex.g4 into
packages/goal-tree/grammar and add a documented npm script (e.g.,
"generate:antlr") that runs the ANTLR generator to produce
RTRegexLexer.ts/related files in packages/goal-tree/src/antlr, plus a README
note on regeneration; pick one approach and implement it so the authoritative
grammar and a clear regeneration procedure live with the generated outputs
(reference RTRegexLexer.ts and RTRegex.g4 when making the change).

In `@packages/goal-tree/src/GoalTree.ts`:
- Around line 70-73: fromModel currently skips model validation causing unmarked
roots; update GoalTree.fromModel to validate the incoming Model the same way
fromFile/fromJSON do by calling validateModel(model) before calling
convertToTree(model), handle validation errors consistently (throw or propagate
the same error type as fromFile/fromJSON), and only construct the GoalTree with
convertToTree(model) after validation succeeds so this.root will be correctly
set; reference functions: fromModel, validateModel, convertToTree, fromFile,
fromJSON, and the GoalTree constructor/root behavior.

In `@packages/goal-tree/src/internal/creation.ts`:
- Around line 76-78: pendingDependencies is module-scoped and can leak stale IDs
across failed conversions; make it local to the conversion flow or ensure it's
cleared in a try/finally around the tree creation so each run is isolated.
Locate the usage of pendingDependencies in the conversion/creation routine (and
the other occurrences around the 555-575 area) and either move the Map
declaration inside the function that performs the conversion or wrap the
conversion logic in try { ... } finally { pendingDependencies.clear(); } so
pendingDependencies cannot persist after an exception.
- Around line 522-531: The resolution of rawDependsOn in the
resolvedDependencies block currently casts any found node to GoalNode; update
this to validate that each depNode is actually a Goal (e.g., check a
distinguishing property or type such as depNode.type === 'goal' or use an
instanceof/TypeGuard for GoalNode) before casting. For each depId, if
nodeMap.get(depId) returns undefined keep the existing error, but if it returns
a node that is not a Goal, throw a clear error mentioning the dependency id and
node.id (same format as the existing error) stating the dependency is not a
Goal. Modify the resolvedDependencies mapping (and any helper TypeGuard if
present) to perform this check and only return depNode as GoalNode after
validation.

In `@packages/goal-tree/src/internal/variables.ts`:
- Around line 7-8: The helper achievableFormulaVariable is duplicated; export
the existing function (achievableFormulaVariable) from the goal-tree package
module where it's defined, then remove the local duplicate in the lib package's
templateEngine/common.ts and replace it with an import from the goal-tree
export; ensure the exported symbol signature matches the usage in
templateEngine/common.ts and update any imports/exports so TypeScript resolves
the symbol from the goal-tree package.

In `@packages/lib/src/templateEngine/modules/changeManager/changeManager.ts`:
- Around line 14-21: The calls to GoalTree.static helpers are passing the
GoalTree instance `gm` but these helpers (`GoalTree.allByType` and
`GoalTree.taskAchievabilityVariables`) expect a GoalTreeArray (`TreeNode[]`);
update the calls to pass `gm.nodes` instead (i.e., replace
`GoalTree.allByType(gm, 'task')` with `GoalTree.allByType(gm.nodes, 'task')` and
`GoalTree.taskAchievabilityVariables(gm)` with
`GoalTree.taskAchievabilityVariables(gm.nodes)`) so the static methods receive
the expected array and any internal array operations like flatMap will work.

In `@packages/lib/src/templateEngine/modules/goalModule/template/pursue/index.ts`:
- Around line 62-65: The call to pursueLogger.goalDependency currently assumes
goal.properties.edge.dependsOn is always defined and uses .map, which can throw;
update the call site (pursueLogger.goalDependency with goal.id and
goal.properties.edge.dependsOn.map(...)) to guard against undefined by using
optional chaining or a fallback array (e.g., (goal.properties.edge.dependsOn ??
[]).map(...) or goal.properties.edge.dependsOn?.map(...) ?? []) so it safely
passes an empty array when no dependencies exist.
🧹 Nitpick comments (8)
packages/goal-tree/src/internal/utils.ts (1)

21-25: Dead code: the !first check is unreachable.

After the arrays.length === 0 check on line 16, first is guaranteed to be defined (though possibly an empty array). Since ![] is false, this branch never executes for well-typed input.

The function is functionally correct; this is just a minor cleanup opportunity.

♻️ Suggested simplification
   const [first, ...rest] = arrays;
-  if (!first) {
-    yield [];
-    return;
-  }
+  // first is guaranteed defined after length check; empty array yields nothing (correct)
   for (const item of first) {
packages/goal-tree/src/Model.ts (1)

7-47: validateModel has a side effect: it mutates the model to mark the root node.

The function sets node.customProperties.root = 'true' on line 37, making it both a validator and a mutator. Consider documenting this behavior in the JSDoc or renaming to validateAndMarkRoot to make the side effect explicit.

📝 Suggested documentation update
 /**
  * Validate an iStar model
+ * Also marks the root node by setting customProperties.root = 'true'
  * `@throws` Error if the model is invalid
  */
 function validateModel(model: IStarModel): void {
packages/goal-tree/src/types/istar.ts (1)

93-103: Consider documenting the never[] fields.

The orphans: never[] and dependencies: never[] indicate these arrays should always be empty. A brief comment explaining why (e.g., "unused in goal models" or "preserved for piStar compatibility") would help future maintainers understand the intent.

📝 Suggested documentation
 export type Model = {
   actors: Actor[];
+  /** Always empty - preserved for piStar format compatibility */
   orphans: never[];
+  /** Always empty - preserved for piStar format compatibility */
   dependencies: never[];
   links: Link[];
packages/ui/lib/models/goal.ts (1)

31-56: Type safety relies on runtime validation.

The JSON.parse result is assigned directly to IStarModel without compile-time type checking. While Model.validate(model) provides runtime validation, consider using a type guard or schema validation library (like Zod) that provides both runtime validation and compile-time type narrowing.

Current approach is acceptable since validation occurs immediately after parsing.

packages/goal-tree/src/internal/traversal.ts (1)

47-66: Consider using a Map for deduplication to improve performance.

The current implementation creates a new object on each reduce iteration via spread. For large trees, this is O(n²). Using a Map directly would be more efficient.

♻️ Optional performance improvement
 export function allByType(gm: GoalTree, type: Type): TreeNode[] {
-  const allCurrent = gm
+  const allNodes = gm
     .flatMap((node) => {
       const children = childrenWithTasksAndResources(node);
       if (children.length > 0) {
         return [node, ...(allByType(children, type) ?? [])];
       }
       return [node];
     })
     .filter((node) => node.type === type)
-    .sort((a, b) => a.id.localeCompare(b.id))
-    .reduce<Record<string, TreeNode>>((acc, current) => {
-      if (acc[current.id]) {
-        return { ...acc };
-      }
-      return { ...acc, [current.id]: current };
-    }, {});
+    .sort((a, b) => a.id.localeCompare(b.id));
 
-  return Object.values(allCurrent);
+  // Deduplicate using Map for O(n) instead of O(n²)
+  const seen = new Map<string, TreeNode>();
+  for (const node of allNodes) {
+    if (!seen.has(node.id)) {
+      seen.set(node.id, node);
+    }
+  }
+  return Array.from(seen.values());
 }
packages/lib/src/prismValidator/validator.ts (1)

216-228: Consider simplifying the nested ternary chain.

The deeply nested ternary for determining goalType is hard to read. A lookup or early return pattern would improve clarity.

♻️ Suggested refactor
-    const goalType =
-      goal.properties.edge.executionDetail?.type === 'choice'
-        ? 'choice'
-        : goal.properties.edge.executionDetail?.type === 'degradation'
-          ? 'degradation'
-          : goal.properties.edge.executionDetail?.type === 'sequence'
-            ? 'sequence'
-            : goal.properties.edge.executionDetail?.type === 'interleaved'
-              ? 'interleaved'
-              : goal.properties.edge.executionDetail?.type === 'alternative'
-                ? 'alternative'
-                : 'basic';
+    const executionType = goal.properties.edge.executionDetail?.type;
+    const goalType: keyof GoalTypeCounts =
+      executionType && executionType in goalTypes ? executionType : 'basic';
packages/goal-tree/src/GoalTree.ts (1)

127-147: Debug print() method may not belong in production SDK.

The print() method uses console.log extensively and assumes the first node is a GoalNode with children. Consider marking it as @internal or @deprecated, or moving it to a separate debug utility.

packages/lib/src/antlr/RTRegexListener.ts (1)

1-21: Consider consolidating ANTLR artifacts to avoid drift.

This listener file appears to be duplicated in both packages/lib/src/antlr/ and packages/goal-tree/src/antlr/. If the grammar (RTRegex.g4) is updated in the future, both locations would need regeneration.

Consider either:

  1. Having packages/lib re-export the listener from @goal-controller/goal-tree, or
  2. Keeping a single source of truth for the grammar and generated files

If the duplication is intentional (e.g., different consumers or versioning requirements), please disregard.

Comment on lines +1 to +14
// Generated from grammar/RTRegex.g4 by ANTLR 4.13.2
// noinspection ES6UnusedImports,JSUnusedGlobalSymbols,JSUnusedLocalSymbols
import {
ATN,
ATNDeserializer,
CharStream,
DecisionState,
DFA,
Lexer,
LexerATNSimulator,
RuleContext,
PredictionContextCache,
Token,
} from 'antlr4';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if both RTRegexLexer files exist and compare their content
echo "=== Checking for RTRegexLexer in both locations ==="
fd -t f "RTRegexLexer.ts" packages/

echo ""
echo "=== Checking if files are identical ==="
if [ -f "packages/goal-tree/src/antlr/RTRegexLexer.ts" ] && [ -f "packages/lib/src/antlr/RTRegexLexer.ts" ]; then
  if diff -q "packages/goal-tree/src/antlr/RTRegexLexer.ts" "packages/lib/src/antlr/RTRegexLexer.ts"; then
    echo "Files are identical"
  else
    echo "Files differ"
    echo ""
    echo "=== First 50 lines of goal-tree version ==="
    head -50 "packages/goal-tree/src/antlr/RTRegexLexer.ts"
    echo ""
    echo "=== First 50 lines of lib version ==="
    head -50 "packages/lib/src/antlr/RTRegexLexer.ts"
  fi
else
  echo "One or both files do not exist"
  [ ! -f "packages/goal-tree/src/antlr/RTRegexLexer.ts" ] && echo "Missing: packages/goal-tree/src/antlr/RTRegexLexer.ts"
  [ ! -f "packages/lib/src/antlr/RTRegexLexer.ts" ] && echo "Missing: packages/lib/src/antlr/RTRegexLexer.ts"
fi

Repository: vieirin/goal-controller

Length of output: 263


🏁 Script executed:

#!/bin/bash
# Check package.json files to understand dependencies
echo "=== packages/goal-tree/package.json ==="
[ -f "packages/goal-tree/package.json" ] && cat "packages/goal-tree/package.json" || echo "Not found"

echo ""
echo "=== Check imports of RTRegexLexer in goal-tree ==="
rg "RTRegexLexer" packages/goal-tree/ -t ts --max-count 10

echo ""
echo "=== Check imports of RTRegexLexer in lib ==="
rg "RTRegexLexer" packages/lib/ -t ts --max-count 10

echo ""
echo "=== Check if goal-tree depends on lib ==="
grep -i "\"lib\"" "packages/goal-tree/package.json" || echo "No direct dependency on lib found"

Repository: vieirin/goal-controller

Length of output: 2753


🏁 Script executed:

#!/bin/bash
# Check for other ANTLR files in both packages
echo "=== ANTLR files in goal-tree ==="
fd -t f "RTRegex" packages/goal-tree/src/antlr/ | sort

echo ""
echo "=== ANTLR files in lib ==="
fd -t f "RTRegex" packages/lib/src/antlr/ | sort

echo ""
echo "=== Check if files are identical (Parser, Listener, Visitor, etc.) ==="
for file in RTRegexLexer.ts RTRegexParser.ts RTRegexParserVisitor.ts RTRegexParserListener.ts; do
  if [ -f "packages/goal-tree/src/antlr/$file" ] && [ -f "packages/lib/src/antlr/$file" ]; then
    echo -n "$file: "
    if diff -q "packages/goal-tree/src/antlr/$file" "packages/lib/src/antlr/$file" > /dev/null; then
      echo "identical"
    else
      echo "DIFFER"
    fi
  fi
done

echo ""
echo "=== Check README or docs for package extraction intent ==="
find packages/goal-tree -name "README*" -type f | head -5 | xargs cat 2>/dev/null | head -20

Repository: vieirin/goal-controller

Length of output: 574


🏁 Script executed:

#!/bin/bash
# Check for ANTLR grammar source files
echo "=== Looking for RTRegex.g4 grammar files ==="
fd -t f "RTRegex.g4" packages/

echo ""
echo "=== Check all ANTLR files across both packages ==="
echo "goal-tree ANTLR directory:"
ls -la packages/goal-tree/src/antlr/ 2>/dev/null || echo "Directory not found"

echo ""
echo "lib ANTLR directory:"
ls -la packages/lib/src/antlr/ 2>/dev/null || echo "Directory not found"

echo ""
echo "=== Check git log for recent changes to these files ==="
git log --oneline -10 -- "packages/goal-tree/src/antlr/" "packages/lib/src/antlr/" 2>/dev/null || echo "No recent changes tracked"

Repository: vieirin/goal-controller

Length of output: 1525


Address ANTLR file duplication without grammar source.

The identical RTRegexLexer.ts and other ANTLR files are duplicated in packages/goal-tree/src/antlr/, but the grammar source (RTRegex.g4) only exists in packages/lib/grammar/. This creates a maintenance risk: if the grammar is updated in lib, the generated files in goal-tree won't be regenerated automatically, causing synchronization drift.

Either copy the grammar source to goal-tree with a documented regeneration procedure, or have goal-tree depend on lib for these ANTLR utilities.

🤖 Prompt for AI Agents
In `@packages/goal-tree/src/antlr/RTRegexLexer.ts` around lines 1 - 14, The
generated ANTLR files in packages/goal-tree/src/antlr (e.g., RTRegexLexer.ts)
are duplicated while the grammar source RTRegex.g4 lives only in
packages/lib/grammar; fix by either (A) removing the generated files from
goal-tree and making goal-tree depend on lib’s ANTLR artifacts (export the
generated JS/TS from packages/lib and import them in goal-tree, updating
package.json to add that dependency and removing RTRegexLexer.ts and sibling
files), or (B) copy packages/lib/grammar/RTRegex.g4 into
packages/goal-tree/grammar and add a documented npm script (e.g.,
"generate:antlr") that runs the ANTLR generator to produce
RTRegexLexer.ts/related files in packages/goal-tree/src/antlr, plus a README
note on regeneration; pick one approach and implement it so the authoritative
grammar and a clear regeneration procedure live with the generated outputs
(reference RTRegexLexer.ts and RTRegex.g4 when making the change).

Comment on lines +70 to +73
static fromModel(model: Model): GoalTree {
const nodes = convertToTree(model);
return new GoalTree(nodes);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

fromModel bypasses model validation.

fromFile and fromJSON call validateModel, but fromModel does not. If a caller passes an unvalidated Model directly, the root node won't be marked and this.root will return undefined.

🛠️ Suggested fix
  static fromModel(model: Model): GoalTree {
+   validateModel(model);
    const nodes = convertToTree(model);
    return new GoalTree(nodes);
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static fromModel(model: Model): GoalTree {
const nodes = convertToTree(model);
return new GoalTree(nodes);
}
static fromModel(model: Model): GoalTree {
validateModel(model);
const nodes = convertToTree(model);
return new GoalTree(nodes);
}
🤖 Prompt for AI Agents
In `@packages/goal-tree/src/GoalTree.ts` around lines 70 - 73, fromModel currently
skips model validation causing unmarked roots; update GoalTree.fromModel to
validate the incoming Model the same way fromFile/fromJSON do by calling
validateModel(model) before calling convertToTree(model), handle validation
errors consistently (throw or propagate the same error type as
fromFile/fromJSON), and only construct the GoalTree with convertToTree(model)
after validation succeeds so this.root will be correctly set; reference
functions: fromModel, validateModel, convertToTree, fromFile, fromJSON, and the
GoalTree constructor/root behavior.

Comment on lines 522 to 531
const resolvedDependencies = rawDependsOn.map((depId) => {
const depNode = nodeMap.get(depId);
if (!depNode) {
throw new Error(
`[INVALID MODEL]: Dependency ${depId} not found for node ${node.id}`,
);
}
return depNode;
return depNode as GoalNode;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate that dependsOn targets are goals.

dependsOn is typed as GoalNode[], but resolution currently casts any node. If a model references a task/resource, the graph becomes inconsistent. Add a type check and throw.

✅ Suggested validation
     const resolvedDependencies = rawDependsOn.map((depId) => {
       const depNode = nodeMap.get(depId);
-      if (!depNode) {
+      if (!depNode || depNode.type !== 'goal') {
         throw new Error(
-          `[INVALID MODEL]: Dependency ${depId} not found for node ${node.id}`,
+          `[INVALID MODEL]: Dependency ${depId} must reference a goal node`,
         );
       }
-      return depNode as GoalNode;
+      return depNode;
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const resolvedDependencies = rawDependsOn.map((depId) => {
const depNode = nodeMap.get(depId);
if (!depNode) {
throw new Error(
`[INVALID MODEL]: Dependency ${depId} not found for node ${node.id}`,
);
}
return depNode;
return depNode as GoalNode;
});
const resolvedDependencies = rawDependsOn.map((depId) => {
const depNode = nodeMap.get(depId);
if (!depNode || depNode.type !== 'goal') {
throw new Error(
`[INVALID MODEL]: Dependency ${depId} must reference a goal node`,
);
}
return depNode;
});
🤖 Prompt for AI Agents
In `@packages/goal-tree/src/internal/creation.ts` around lines 522 - 531, The
resolution of rawDependsOn in the resolvedDependencies block currently casts any
found node to GoalNode; update this to validate that each depNode is actually a
Goal (e.g., check a distinguishing property or type such as depNode.type ===
'goal' or use an instanceof/TypeGuard for GoalNode) before casting. For each
depId, if nodeMap.get(depId) returns undefined keep the existing error, but if
it returns a node that is not a Goal, throw a clear error mentioning the
dependency id and node.id (same format as the existing error) stating the
dependency is not a Goal. Modify the resolvedDependencies mapping (and any
helper TypeGuard if present) to perform this check and only return depNode as
GoalNode after validation.

Comment on lines +7 to +8
const achievableFormulaVariable = (goalId: string): string =>
`${goalId}_achievable`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all occurrences of achievableFormulaVariable
rg -n "achievableFormulaVariable" --type=ts

Repository: vieirin/goal-controller

Length of output: 1756


🏁 Script executed:

#!/bin/bash
# Read the lib version
echo "=== packages/lib/src/templateEngine/common.ts ==="
sed -n '10,20p' packages/lib/src/templateEngine/common.ts

echo -e "\n=== packages/goal-tree/src/internal/variables.ts (first 30 lines) ==="
head -30 packages/goal-tree/src/internal/variables.ts

Repository: vieirin/goal-controller

Length of output: 1454


🏁 Script executed:

#!/bin/bash
# Check if goal-tree imports from lib
echo "=== Checking imports in goal-tree/src/internal/variables.ts ==="
head -10 packages/goal-tree/src/internal/variables.ts

# Check dependencies in goal-tree package.json
echo -e "\n=== goal-tree package.json dependencies ==="
cat packages/goal-tree/package.json | jq '.dependencies // {}'

# Check if lib is a dependency of goal-tree
echo -e "\n=== Does goal-tree depend on lib? ==="
cat packages/goal-tree/package.json | jq '.dependencies | has("@goal-controller/lib")'

Repository: vieirin/goal-controller

Length of output: 552


🏁 Script executed:

#!/bin/bash
# Check if lib depends on goal-tree
echo "=== lib package.json dependencies ==="
cat packages/lib/package.json | jq '.dependencies // {}'

echo -e "\n=== Does lib depend on goal-tree? ==="
cat packages/lib/package.json | jq '.dependencies | has("@goal-controller/goal-tree")'

Repository: vieirin/goal-controller

Length of output: 357


Refactor to avoid duplication: export achievableFormulaVariable from goal-tree and import in lib.

This helper is currently duplicated across both packages with identical implementations. Since lib already depends on goal-tree, the viable solution is to:

  1. Export achievableFormulaVariable from packages/goal-tree/src/internal/variables.ts
  2. Import it in packages/lib/src/templateEngine/common.ts instead of defining a local copy

This prevents divergence if the naming convention changes and eliminates unnecessary duplication.

🤖 Prompt for AI Agents
In `@packages/goal-tree/src/internal/variables.ts` around lines 7 - 8, The helper
achievableFormulaVariable is duplicated; export the existing function
(achievableFormulaVariable) from the goal-tree package module where it's
defined, then remove the local duplicate in the lib package's
templateEngine/common.ts and replace it with an import from the goal-tree
export; ensure the exported symbol signature matches the usage in
templateEngine/common.ts and update any imports/exports so TypeScript resolves
the symbol from the goal-tree package.

- Replace edge and sleec properties with a single generic engine property
- Add EngineMapper interface for mapping raw iStar properties to engine-specific props
- Create EdgeGoalProps/EdgeTaskProps in templateEngine/types.ts
- Create SleecGoalProps/SleecTaskProps in sleecTemplateEngine/types.ts
- Implement edgeEngineMapper and sleecEngineMapper
- Update GoalTree class to require an EngineMapper when creating trees
- Update all template engine files to use properties.engine
- Update CLI and UI to use appropriate engine mappers

BREAKING CHANGE: GoalTree.fromModel now requires an EngineMapper argument
- Update prismValidator tests to pass edgeEngineMapper to GoalTree.fromModel
- Update sleec tests to use SleecTask/SleecGoalTree types with engine property
- Update goalDependency test to use engine property instead of edge
- Fix implicit any types in pursue/index.ts with type guards
- Fix implicit any types in pursue/orGoal.ts with proper type annotations
- Fix implicit any types in variables.ts with proper type annotations
- Enable noEmitOnError to fail build on type errors
- Explicitly enable all strict mode flags to match Vercel's environment
- cli.ts: Validate variables file loaded before running model in MODE=last
- taskAchievabilityVariables.ts: Log warning when using default achievability
- tasks/variables.ts: Guard maxRetries with strict finite positive integer check
- goalModules.ts: Use goalNumberId helper instead of inline regex for sorting
- expectedElements.ts: Filter out resources when building choice variables
  and pursue transitions (resources cannot be pursued)
The README referenced a non-existent packages/lib/src/engines/sleec/template/README.md file.
Updated to point to the existing 'Transformation Example (SLEEC)' section in the main README
which already contains comprehensive SLEEC engine documentation.
The previous check 'if (!children)' never triggered because Node.children()
always returns an array (possibly empty), and arrays are truthy. Updated to
check 'children.length === 0' to properly fail fast when a choice goal has
no pursueable children.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/lib/src/cli.ts (1)

86-90: Missing return after recursive mainMenu() call.

When no files are found, mainMenu() is called recursively but execution continues to the inquirer.prompt below, which will fail or behave unexpectedly with an empty files array.

🐛 Proposed fix
     if (files.length === 0) {
       console.log('No files found in the example directory.');
       await mainMenu();
+      return;
     }
🤖 Fix all issues with AI agents
In `@packages/lib/src/engines/edge/template/modules/goalModule/goalModules.ts`:
- Around line 5-12: The goalNumberId function currently uses goalId.match(/\d+/)
which allows digits anywhere (e.g., "foo1"); change the validation to require
the "G" prefix by matching the whole string against a pattern like /^G(\d+)$/
(or case-insensitive if needed) and return the captured digit group; update the
thrown error message to reflect the enforced pattern and ensure you reference
goalNumberId when making the change.

In
`@packages/lib/src/engines/edge/template/modules/goalModule/template/pursue/index.ts`:
- Line 27: The cast `dep as EdgeGoalNode` masks a type mismatch between the
declared type of dependsOn and what hasBeenAchieved requires; update the type of
dependsOn (the collection type used where dependsOn is declared) to use the more
specific GoalNode<EdgeGoalPropsResolved, EdgeTaskProps, EdgeResourceProps>
(EdgeGoalNode) so the map call can pass each dep directly to hasBeenAchieved, or
alternatively add a proper type guard function (e.g., isEdgeGoalNode(dep): dep
is EdgeGoalNode) and use .filter/is guard before .map to narrow the type; adjust
the declaration of dependsOn or add the guard where .map((dep) =>
hasBeenAchieved(...)) is called so the explicit cast is no longer necessary.

In `@README.md`:
- Around line 431-451: The example call passes tree.nodes but the function
signature is sleecTemplateEngine(tree: GoalTreeType<...>); fix the example by
calling sleecTemplateEngine(tree) (remove .nodes) so the passed value matches
the expected GoalTreeType used by sleecTemplateEngine and its internal calls to
GoalTree.allByType; update the example lines with
Model.load/GoalTree.fromModel/sleecEngineMapper accordingly.
🧹 Nitpick comments (5)
packages/lib/src/cli.ts (3)

74-84: Consider validating loaded variables for consistency with MODE=last.

The MODE=last flow (lines 40-51) validates that variables are non-empty after loading, but the interactive "last" action does not. If inputDefaultVariables fails or user cancels, loadVariables may return {} and runModel proceeds with empty variables.

♻️ Suggested consistency improvement
     const variables = loadVariables(lastSelectedModel);
+    if (Object.keys(variables).length === 0) {
+      console.error('Failed to load variables. Please try again.');
+      return;
+    }
     await runModel(lastSelectedModel, variables, cleanFlag);

132-132: Prompt message should reference "dump" not "run".

The message says "Select a model file to run:" but this is the dumpTree action.

✏️ Suggested fix
-        message: 'Select a model file to run:',
+        message: 'Select a model file to dump:',

141-144: Consider adding error handling for model loading.

Model.load and GoalTree.fromModel may throw if the model file is invalid or malformed. Wrapping in try-catch would provide a more user-friendly error message instead of a stack trace.

🛡️ Suggested error handling
+    try {
       const model = Model.load(selectedFile);
       const tree = GoalTree.fromModel(model, edgeEngineMapper);
       const json = tree.toJSON();
       await writeFile(`output/${path.parse(selectedFile).name}.json`, json);
       console.log(
         `The file ${path.parse(selectedFile).name}.json was saved successfully!`,
       );
+    } catch (error) {
+      console.error(`Failed to process model file: ${error instanceof Error ? error.message : error}`);
+    }
packages/lib/src/engines/edge/template/modules/changeManager/template/achievabilityVariables/taskAchievabilityVariables.ts (1)

28-28: Remove unreachable ?? -1 fallback.

At line 28, variableValue is guaranteed to be either the configured value or DEFAULT_ACHIEVABILITY (0.5) due to the ternary on lines 16-18. The ?? -1 fallback is dead code and may mislead readers into thinking variableValue can be nullish.

🧹 Proposed cleanup
-  logger.achievabilityTaskConstant(task.id, variableName, variableValue ?? -1);
+  logger.achievabilityTaskConstant(task.id, variableName, variableValue);
packages/lib/src/engines/edge/template/modules/goalModule/template/pursue/index.ts (1)

385-385: Unnecessary nullish coalescing.

pursueLines is the result of a .map() chain, which always returns an array (never null/undefined). The ?? [] fallback is unreachable.

♻️ Suggested simplification
-  return pursueLines ?? [];
+  return pursueLines;

- goalModules.ts: Update goalNumberId to require G/T/R prefix pattern
  (e.g., 'G1', 'T5', 'R3') instead of allowing digits anywhere in string
- pursue/index.ts: Add isEdgeGoalNode type guard to properly narrow
  dependsOn type instead of using unsafe 'as EdgeGoalNode' cast
- README.md: Fix sleecTemplateEngine example to pass 'tree' instead
  of 'tree.nodes' to match the function signature
Remove intermediate mapperCreator variable by chaining the curried
createEngineMapper calls directly, improving code readability while
preserving full type inference.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

588-590: Fix typo in directory name.

Line 588 says "goalmngt folder" but line 589 shows "goalmgmt/edgeModel.txt". The directory name is inconsistent.

📝 Suggested fix
-3. Add the goal model to the goalmngt folder
+3. Add the goal model to the goalmgmt folder
🤖 Fix all issues with AI agents
In `@packages/lib/src/engines/edge/mapper.ts`:
- Around line 57-80: The parseDecision function currently allows empty variable
names (e.g., ":3" or " ,:2") to pass through; update parseDecision to trim the
variable portion (d[0]) and validate it is non-empty after trimming, throwing a
clear Error like `[INVALID DECISION]: variable must be a non-empty string` for
any entry with an empty trimmed variable; keep existing numeric validation for
space (parseInt) and use the trimmed variable when returning the final objects
so templates never receive empty variable names.
- Around line 87-101: In parseMaxRetries, reject non-strict integer strings
before parsing: trim the input maxRetries and validate it matches a whole-number
regex (e.g. /^\d+$/) so values like "5abc" or "1.5" fail; if the trimmed string
doesn't match, throw the same Error using nodeType, otherwise safely parseInt
and return the number (preserve the existing undefined -> 0 behavior). Ensure
you update the logic inside the parseMaxRetries function to do the trim + regex
check prior to parsing.

In `@README.md`:
- Around line 264-284: The README shows inconsistent query APIs—use one approach
consistently (prefer the instance API) by replacing static calls like
GoalTree.allByType(tree, 'task') in the "generatePurposes" and "Complete
Template Engine" examples with the instance form tree.query.allByType('task'),
and update any other occurrences (e.g., GoalTree.leafGoals(...) →
tree.query.leafGoals()) so all examples use tree.query.*; alternatively, if you
intend to support both patterns, add a short paragraph explaining when to use
GoalTree.static methods vs the tree.query instance methods and keep both
examples annotated accordingly.

Comment on lines +57 to +80
const parseDecision = (
decision: string | undefined,
): Array<{ variable: string; space: number }> => {
if (!decision) {
return [];
}
const parsedDecision = decision.split(',').map((d) => d.split(':'));
parsedDecision.forEach((d) => {
if (d.length !== 2) {
throw new Error(
`[INVALID DECISION]: decision must be a variable and space: got ${decision}, expected format variable:space`,
);
}
if (isNaN(parseInt(d[1] ?? ''))) {
throw new Error(
`[INVALID DECISION]: space must be a number: got ${d[1]}`,
);
}
});

return parsedDecision.map((d) => ({
variable: d[0]?.trim() ?? '',
space: parseInt(d[1] ?? '', 10),
}));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reject empty decision variable names.
Inputs like ":3" or " ,:2" currently produce an empty variable string, which will leak into templates.

🔧 Suggested fix
   const parsedDecision = decision.split(',').map((d) => d.split(':'));
   parsedDecision.forEach((d) => {
     if (d.length !== 2) {
       throw new Error(
         `[INVALID DECISION]: decision must be a variable and space: got ${decision}, expected format variable:space`,
       );
     }
+    const variable = (d[0] ?? '').trim();
+    if (!variable) {
+      throw new Error(
+        `[INVALID DECISION]: variable name must be non-empty: got ${decision}`,
+      );
+    }
     if (isNaN(parseInt(d[1] ?? ''))) {
       throw new Error(
         `[INVALID DECISION]: space must be a number: got ${d[1]}`,
       );
     }
   });
@@
   return parsedDecision.map((d) => ({
-    variable: d[0]?.trim() ?? '',
+    variable: d[0]?.trim() ?? '',
     space: parseInt(d[1] ?? '', 10),
   }));
🤖 Prompt for AI Agents
In `@packages/lib/src/engines/edge/mapper.ts` around lines 57 - 80, The
parseDecision function currently allows empty variable names (e.g., ":3" or "
,:2") to pass through; update parseDecision to trim the variable portion (d[0])
and validate it is non-empty after trimming, throwing a clear Error like
`[INVALID DECISION]: variable must be a non-empty string` for any entry with an
empty trimmed variable; keep existing numeric validation for space (parseInt)
and use the trimmed variable when returning the final objects so templates never
receive empty variable names.

Comment on lines +87 to +101
const parseMaxRetries = (
maxRetries: string | undefined,
nodeType: 'goal' | 'task',
): number => {
if (!maxRetries) {
return 0;
}
const parsed = parseInt(maxRetries, 10);
if (isNaN(parsed) || parsed < 0) {
throw new Error(
`[INVALID ${nodeType.toUpperCase()}]: maxRetries must be a non-negative integer: got "${maxRetries}"`,
);
}
return parsed;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

JavaScript parseInt semantics "1.5" "5abc" strict integer validation

💡 Result:

  • parseInt(str, radix) parses up to the first non-valid digit (after optional whitespace and +/-). It does not require the whole string to be an integer. If it hits an invalid character, it stops and returns what it has so far; if it can’t parse any digit at the start, it returns NaN. Also, . is not a digit, so decimals get truncated. [1]
    • parseInt("1.5", 10)1 (stops at .) [1]
    • parseInt("5abc", 10)5 (stops at a) [1]

Strict integer validation (string must be only an integer)

Use a full-string check plus numeric validation:

const isStrictIntString = s => /^[-+]?\d+$/.test(s);

If you want an actual number too:

const parseStrictInt = s => {
  if (!/^[-+]?\d+$/.test(s)) return null;     // reject "1.5", "5abc", " 5 "
  const n = Number(s);
  return Number.isInteger(n) ? n : null;
};

Number.isInteger(x) returns true only if x is a number and an integer (not NaN/Infinity). [2]

Why not just Number()? Because Number("5abc") becomes NaN (good), but you still typically want the regex to enforce the exact accepted format (sign/digits only) and reject things like whitespace if desired.

Sources: MDN on parseInt() behavior and stopping at invalid characters / truncating at . [1]; MDN on Number.isInteger() semantics [2].

References:
[1] MDN — parseInt() (Global object)
[2] MDN — Number.isInteger()


🏁 Script executed:

# Find usages of parseMaxRetries in the codebase to understand impact
rg "parseMaxRetries" --type ts --type tsx -B 2 -A 2

Repository: vieirin/goal-controller

Length of output: 94


🏁 Script executed:

# Check if there are any tests for parseMaxRetries
rg "parseMaxRetries" --type ts --type tsx --type json

Repository: vieirin/goal-controller

Length of output: 94


🏁 Script executed:

# Search for parseMaxRetries usage
rg "parseMaxRetries" -t ts

Repository: vieirin/goal-controller

Length of output: 330


🏁 Script executed:

# Check for any test files mentioning this function
find . -type f -name "*.test.ts" -o -name "*.spec.ts" | xargs rg "parseMaxRetries" 2>/dev/null || echo "No test files found with parseMaxRetries"

Repository: vieirin/goal-controller

Length of output: 107


🏁 Script executed:

# Check the context around where parseMaxRetries is defined and used
rg "parseMaxRetries|maxRetries" -t ts -B 3 -A 3 | head -100

Repository: vieirin/goal-controller

Length of output: 6345


🏁 Script executed:

# Verify the file content and exact line numbers to ensure diff accuracy
sed -n '85,105p' packages/lib/src/engines/edge/mapper.ts

Repository: vieirin/goal-controller

Length of output: 630


Enforce strict integer parsing for maxRetries.

parseInt truncates at the first non-digit character, so parseInt("5abc", 10) returns 5 and parseInt("1.5", 10) returns 1—both valid numbers. The isNaN check won't catch these cases. Invalid strings like "5abc" or "1.5" will pass validation even though the error message requires "a non-negative integer."

🔧 Suggested fix
 const parseMaxRetries = (
   maxRetries: string | undefined,
   nodeType: 'goal' | 'task',
 ): number => {
   if (!maxRetries) {
     return 0;
   }
-  const parsed = parseInt(maxRetries, 10);
-  if (isNaN(parsed) || parsed < 0) {
+  if (!/^\d+$/.test(maxRetries)) {
     throw new Error(
       `[INVALID ${nodeType.toUpperCase()}]: maxRetries must be a non-negative integer: got "${maxRetries}"`,
     );
   }
-  return parsed;
+  return Number(maxRetries);
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const parseMaxRetries = (
maxRetries: string | undefined,
nodeType: 'goal' | 'task',
): number => {
if (!maxRetries) {
return 0;
}
const parsed = parseInt(maxRetries, 10);
if (isNaN(parsed) || parsed < 0) {
throw new Error(
`[INVALID ${nodeType.toUpperCase()}]: maxRetries must be a non-negative integer: got "${maxRetries}"`,
);
}
return parsed;
};
const parseMaxRetries = (
maxRetries: string | undefined,
nodeType: 'goal' | 'task',
): number => {
if (!maxRetries) {
return 0;
}
if (!/^\d+$/.test(maxRetries)) {
throw new Error(
`[INVALID ${nodeType.toUpperCase()}]: maxRetries must be a non-negative integer: got "${maxRetries}"`,
);
}
return Number(maxRetries);
};
🤖 Prompt for AI Agents
In `@packages/lib/src/engines/edge/mapper.ts` around lines 87 - 101, In
parseMaxRetries, reject non-strict integer strings before parsing: trim the
input maxRetries and validate it matches a whole-number regex (e.g. /^\d+$/) so
values like "5abc" or "1.5" fail; if the trimmed string doesn't match, throw the
same Error using nodeType, otherwise safely parseInt and return the number
(preserve the existing undefined -> 0 behavior). Ensure you update the logic
inside the parseMaxRetries function to do the trim + regex check prior to
parsing.

Comment on lines +264 to +284
#### Querying the Tree

Access typed engine properties through the query interface:

```typescript
// Get all tasks with typed engine properties
const tasks = tree.query.allByType('task');

tasks.forEach(task => {
console.log(task.name); // "Track Vital Signs"
console.log(task.properties.engine.PreCond); // "{dataCollected}"
console.log(task.properties.engine.TriggeringEvent); // "PatientAsleep"
console.log(task.properties.engine.TemporalConstraint); // "300 seconds"
});

// Get all goals
const goals = tree.query.allByType('goal');

// Get leaf goals (goals with tasks, no child goals)
const leafGoals = tree.query.leafGoals();
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

API inconsistency across tutorial examples.

The tutorial demonstrates two different APIs for querying nodes without explanation:

  1. Instance method (lines 270, 280): tree.query.allByType('task')
  2. Static method (lines 390, 434): GoalTree.allByType(tree, 'task')

Within the same "Transformation Example (SLEEC)" section, the "Querying the Tree" subsection shows the instance method, while the "generatePurposes" and "Complete Template Engine" subsections use the static method. This inconsistency will confuse developers following the tutorial.

Either use one API consistently throughout, or explicitly document why both patterns exist and when to use each.

🔄 Suggested fix for consistency

Use the instance method consistently throughout the tutorial:

 const generatePurposes = (tree: SleecGoalTree): string => {
-  const goals = GoalTree.allByType(tree, 'goal');
+  const goals = tree.query.allByType('goal');
 export const sleecTemplateEngine = (
   tree: GoalTreeType<SleecGoalProps, SleecTaskProps>
 ): string => {
-  const tasks = GoalTree.allByType(tree, 'task');
+  const tasks = tree.query.allByType('task');

Also applies to: 379-421, 424-453

🤖 Prompt for AI Agents
In `@README.md` around lines 264 - 284, The README shows inconsistent query
APIs—use one approach consistently (prefer the instance API) by replacing static
calls like GoalTree.allByType(tree, 'task') in the "generatePurposes" and
"Complete Template Engine" examples with the instance form
tree.query.allByType('task'), and update any other occurrences (e.g.,
GoalTree.leafGoals(...) → tree.query.leafGoals()) so all examples use
tree.query.*; alternatively, if you intend to support both patterns, add a short
paragraph explaining when to use GoalTree.static methods vs the tree.query
instance methods and keep both examples annotated accordingly.

- Changed currying order: createEngineMapper<EngineTypes>()({keys, mappers})
- Engine types are now explicit on first call, key types inferred from config
- Use interface for EdgeGoalPropsResolved to enable circular type reference
  (dependsOn[0].properties.engine is properly typed as EdgeGoalPropsResolved)
- Updated edge and sleec mappers to use new pattern
- Updated README documentation to reflect new API
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@packages/goal-tree/src/internal/engineMapper.ts`:
- Around line 153-175: The code currently treats skipResource
(allowedResourceKeys === undefined) as silently dropping any provided
mapResourceProps; update the guard in engineMapper.ts so that if skipResource is
true but config.mapResourceProps is non-undefined you throw a clear error (or
assert) instead of returning a mapper that ignores mapping; reference the
symbols skipResource, config.mapResourceProps, allowedResourceKeys and the
EngineMapper return path to locate where to add the check and error to fail fast
when a consumer supplies mapResourceProps without allowedResourceKeys.

In `@packages/lib/src/cli/menu/selectModel.ts`:
- Around line 20-22: The code in selectModel.ts uses filePath.split('/') to
derive fileName, which breaks on Windows; replace that logic by calling
path.basename(filePath) (ensure the Node 'path' module is imported) and use the
resulting fileName variable in place of the split-based value; keep the existing
error throw for empty/undefined fileName but update the lookup to rely on
path.basename for cross-platform correctness.
🧹 Nitpick comments (2)
packages/lib/src/engines/sleec/mapper.ts (1)

57-68: Inconsistent validation logic with extractSleecGoalProps.

extractSleecGoalProps uses typeof value === 'string' && value.trim() !== '' which rejects whitespace-only strings, while this function uses simple truthy checks that would accept whitespace-only strings. Consider aligning the validation for consistent behavior.

♻️ Proposed refactor using the same pattern
 const extractSleecTaskProps = (raw: RawProps<SleecTaskKey>): SleecTaskProps => {
   const sleecProps: SleecTaskProps = {};
 
-  if (raw.PreCond) sleecProps.PreCond = raw.PreCond;
-  if (raw.TriggeringEvent) sleecProps.TriggeringEvent = raw.TriggeringEvent;
-  if (raw.TemporalConstraint)
-    sleecProps.TemporalConstraint = raw.TemporalConstraint;
-  if (raw.PostCond) sleecProps.PostCond = raw.PostCond;
-  if (raw.ObstacleEvent) sleecProps.ObstacleEvent = raw.ObstacleEvent;
+  for (const key of SLEEC_TASK_KEYS) {
+    const value = raw[key];
+    if (typeof value === 'string' && value.trim() !== '') {
+      sleecProps[key] = value;
+    }
+  }
 
   return sleecProps;
 };
packages/lib/src/engines/edge/mapper.ts (1)

218-298: Consider strict integer parsing for bounds and initialValue.

JavaScript's parseInt() accepts partial numeric strings—parseInt("5abc", 10) returns 5, not NaN—so "5abc", "1.5", and similar malformed inputs silently pass the current isNaN() validation despite failing the empty string check. If you want strict integer-only validation, enforce full-string matching:

♻️ Suggested refactor
-        const lowerBoundInt = parseInt(lowerBound, 10);
-        const upperBoundInt = parseInt(upperBound, 10);
+        const parseStrictInt = (value: string, label: string): number => {
+          const trimmed = value.trim();
+          if (!/^-?\d+$/.test(trimmed)) {
+            throw new Error(
+              `[INVALID RESOURCE]: ${label} must be an integer string, got: "${value}"`,
+            );
+          }
+          return Number.parseInt(trimmed, 10);
+        };
+
+        const lowerBoundInt = parseStrictInt(lowerBound, 'lowerBound');
+        const upperBoundInt = parseStrictInt(upperBound, 'upperBound');
@@
-        const initialValueInt = parseInt(initialValue, 10);
+        const initialValueInt = parseStrictInt(initialValue, 'initialValue');

Comment on lines +153 to +175
const skipResource = config.allowedResourceKeys === undefined;

// If skipResource is explicitly set (no allowedResourceKeys), skip resource mapping
if (skipResource) {
const result: EngineMapper<
TGoalEngine,
TTaskEngine,
TResourceEngine,
TGoalKeys,
TTaskKeys,
TResourceKeys
> = {
allowedGoalKeys: config.allowedGoalKeys,
allowedTaskKeys: config.allowedTaskKeys,
mapGoalProps: config.mapGoalProps,
mapTaskProps: config.mapTaskProps,
afterCreationMapper: config.afterCreationMapper,
skipResource: true,
mapResourceProps: undefined,
allowedResourceKeys: undefined,
};
return result;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against mapResourceProps being silently ignored.
Right now, if allowedResourceKeys is omitted but mapResourceProps is still provided (e.g., JS consumer or any-cast), the mapper gets dropped without feedback. Consider failing fast to avoid subtle runtime misconfigurations.

🔧 Proposed guard
     const skipResource = config.allowedResourceKeys === undefined;
+    if (skipResource && 'mapResourceProps' in config && config.mapResourceProps) {
+      throw new Error(
+        '[INVALID MAPPER]: mapResourceProps was provided but allowedResourceKeys is undefined. ' +
+          'Either provide allowedResourceKeys or remove mapResourceProps.',
+      );
+    }
 
     // If skipResource is explicitly set (no allowedResourceKeys), skip resource mapping
     if (skipResource) {
🤖 Prompt for AI Agents
In `@packages/goal-tree/src/internal/engineMapper.ts` around lines 153 - 175, The
code currently treats skipResource (allowedResourceKeys === undefined) as
silently dropping any provided mapResourceProps; update the guard in
engineMapper.ts so that if skipResource is true but config.mapResourceProps is
non-undefined you throw a clear error (or assert) instead of returning a mapper
that ignores mapping; reference the symbols skipResource,
config.mapResourceProps, allowedResourceKeys and the EngineMapper return path to
locate where to add the check and error to fail fast when a consumer supplies
mapResourceProps without allowedResourceKeys.

Comment on lines 20 to 22
const fileName = filePath.split('/').pop();
if (!fileName) {
throw new Error('File name not found');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

head -40 packages/lib/src/cli/menu/selectModel.ts

Repository: vieirin/goal-controller

Length of output: 1172


Use path.basename for cross-platform filename parsing (Line 20).

filePath.split('/') fails on Windows paths which use backslashes. Replace with path.basename to handle both Unix and Windows paths correctly.

🛠️ Proposed fix
-    const fileName = filePath.split('/').pop();
+    const fileName = path.basename(filePath);
🤖 Prompt for AI Agents
In `@packages/lib/src/cli/menu/selectModel.ts` around lines 20 - 22, The code in
selectModel.ts uses filePath.split('/') to derive fileName, which breaks on
Windows; replace that logic by calling path.basename(filePath) (ensure the Node
'path' module is imported) and use the resulting fileName variable in place of
the split-based value; keep the existing error throw for empty/undefined
fileName but update the lookup to rely on path.basename for cross-platform
correctness.

Renamed the discriminant in RawPropertiesUnion from 'type' to 'nodeType'
to avoid conflicts with user-defined 'type' custom properties (e.g., resource type).
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/goal-tree/src/internal/creation.ts`:
- Around line 350-355: The thrown error message after checking const allEqual =
relations.every((v) => v === relations[0]) contains leading newlines and
indentation; replace the multi-line template literal in the throw statement with
a single-line string (or call .trim() on the template) so the error reads
cleanly, e.g. throw new Error("[INVALID MODEL]: You can't mix and/or
relations"); update the throw in the same block where allEqual and relations are
used.
🧹 Nitpick comments (1)
packages/goal-tree/src/internal/creation.ts (1)

529-535: afterCreationMapper only processes goal nodes; tasks and resources are skipped.

The tree mapping at lines 529-535 only invokes processGoal for nodes where type === 'goal'. If an engine's afterCreationMapper needs to transform task or resource properties post-creation (e.g., resolving cross-references), those transformations won't be applied.

This appears intentional given the current Edge mapper only uses afterCreationMapper for goal-level dependsOn resolution. If future engines require post-creation processing for tasks/resources, this would need to be extended.

Comment on lines +350 to +355
const allEqual = relations.every((v) => v === relations[0]);
if (!allEqual) {
throw new Error(`
[INVALID MODEL]: You can't mix and/or relations
`);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize error message whitespace.

The template literal includes leading newlines and excessive indentation that will appear in the error message.

🔧 Suggested fix
   const allEqual = relations.every((v) => v === relations[0]);
   if (!allEqual) {
-    throw new Error(`
-            [INVALID MODEL]: You can't mix and/or relations
-        `);
+    throw new Error("[INVALID MODEL]: You can't mix and/or relations");
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const allEqual = relations.every((v) => v === relations[0]);
if (!allEqual) {
throw new Error(`
[INVALID MODEL]: You can't mix and/or relations
`);
}
const allEqual = relations.every((v) => v === relations[0]);
if (!allEqual) {
throw new Error("[INVALID MODEL]: You can't mix and/or relations");
}
🤖 Prompt for AI Agents
In `@packages/goal-tree/src/internal/creation.ts` around lines 350 - 355, The
thrown error message after checking const allEqual = relations.every((v) => v
=== relations[0]) contains leading newlines and indentation; replace the
multi-line template literal in the throw statement with a single-line string (or
call .trim() on the template) so the error reads cleanly, e.g. throw new
Error("[INVALID MODEL]: You can't mix and/or relations"); update the throw in
the same block where allEqual and relations are used.

Node's --experimental-strip-types doesn't resolve TypeScript relative
imports without explicit .ts extensions. Since we build before running,
use the compiled JS output instead. Run from project root so relative
paths like 'examples' resolve correctly.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/ui/app/api/transform/route.ts (1)

97-105: ⚠️ Potential issue | 🟡 Minor

Logger not closed on generation errors either.

When generateValidatedPrismModel or sleecTemplateEngine throws, the catch block returns without closing the logger, leaking the resource. Consider using a finally block (or closing in each exit path) to ensure logger.close() is always called.

🧹 Proposed fix — use `finally` for logger cleanup
+    let output: string;
+    let report: LoggerReport | null = null;
     try {
-      // ... engine branches ...
+      // ... engine branches (unchanged) ...
 
       // Get logger report
       report = logger.getReport();
-      logger.close();
 
       // ... dev log + return success ...
     } catch (generationError) {
       console.error('[API] Generation error:', generationError);
       return ApiResponse.error(
         `Generation failed: ${ApiResponse.extractMessage(generationError)}`,
         422,
         ApiResponse.extractDetails(generationError),
       );
+    } finally {
+      logger.close();
     }
packages/lib/src/index.ts (1)

85-97: ⚠️ Potential issue | 🟡 Minor

Use console.error for error output.

Line 90 logs errors to stdout via console.log(err). Errors should go to stderr.

Proposed fix
-        console.log(err);
+        console.error(err);
packages/lib/src/cli.ts (1)

118-123: ⚠️ Potential issue | 🟠 Major

Pre-existing: missing return after recursive mainMenu() when no files found.

When files.length === 0, mainMenu() is called but execution falls through to inquirer.prompt with an empty choices array. The dumpTree branch (Line 158) correctly has return after the same pattern.

Suggested fix
     if (files.length === 0) {
       console.log('No files found in the example directory.');
       await mainMenu();
+      return;
     }
🤖 Fix all issues with AI agents
In `@packages/lib/src/cli.ts`:
- Around line 19-27: The loadVariables function currently swallows parse/read
errors and returns {}, causing corrupted variables to propagate into runModel
for interactive paths; change loadVariables (in packages/lib/src/cli.ts) to
throw the read/parse error instead of returning {} so callers must handle
failures (i.e., replace the catch to rethrow or throw a new Error with context).
Also update the call-sites that invoke loadVariables for the MODE=last branch
and the interactive branches (the code paths checking action === 'last' and
action === 'run' which pass the result into runModel) to catch the thrown error
and exit/return with a clear error message instead of letting an empty object
reach runModel. Ensure references to loadVariables and runModel are used to
locate the changes.

In `@packages/lib/src/index.ts`:
- Around line 78-87: The call to sleecTemplateEngine is being passed tree.nodes
(an array) but sleecTemplateEngine expects the full GoalTreeType so functions
like GoalTree.allByType(tree, 'task') work; update the writeFile invocation to
pass the entire tree object (the variable tree produced by GoalTree.fromModel
and used with Model.load) instead of tree.nodes so sleecTemplateEngine receives
the full structure.
🧹 Nitpick comments (1)
packages/lib/src/index.ts (1)

1-10: Consider separating the CLI entry point from the barrel export.

The shebang (#!/usr/bin/env node) and the CLI block (lines 64–97) live in the same file as all the public re-exports. This couples two distinct responsibilities: being the package's public API surface and being an executable script. A dedicated cli.ts entry point (referenced via "bin" in package.json) would make each concern independently testable and avoid shipping CLI-only imports (like writeFile, path) to library consumers who tree-shake.

Comment on lines +19 to +27
const loadVariables = (modelPath: string): Record<string, boolean | number> => {
const variablesFilePath = getVariablesFilePath(modelPath);
try {
return JSON.parse(readFileSync(variablesFilePath, 'utf8'));
} catch (error) {
console.error('Error reading variables file:', error);
return {};
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

loadVariables silently returns {} on error, but only one call-site validates the result.

The MODE=last path (Lines 73-84) correctly checks for an empty object, but the interactive action === 'last' path (Line 116) and action === 'run' path (Line 149) both pass the result of loadVariables directly into runModel without validation. If the file is corrupt or unreadable (despite existing), an empty {} will silently flow into the PRISM generator.

Consider either:

  1. Having loadVariables throw on error (fail-fast), or
  2. Adding the same empty-check after each loadVariables call.
Option 1: Fail-fast approach
 const loadVariables = (modelPath: string): Record<string, boolean | number> => {
   const variablesFilePath = getVariablesFilePath(modelPath);
-  try {
-    return JSON.parse(readFileSync(variablesFilePath, 'utf8'));
-  } catch (error) {
-    console.error('Error reading variables file:', error);
-    return {};
-  }
+  if (!existsSync(variablesFilePath)) {
+    throw new Error(`Variables file not found: ${variablesFilePath}`);
+  }
+  return JSON.parse(readFileSync(variablesFilePath, 'utf8'));
 };
🤖 Prompt for AI Agents
In `@packages/lib/src/cli.ts` around lines 19 - 27, The loadVariables function
currently swallows parse/read errors and returns {}, causing corrupted variables
to propagate into runModel for interactive paths; change loadVariables (in
packages/lib/src/cli.ts) to throw the read/parse error instead of returning {}
so callers must handle failures (i.e., replace the catch to rethrow or throw a
new Error with context). Also update the call-sites that invoke loadVariables
for the MODE=last branch and the interactive branches (the code paths checking
action === 'last' and action === 'run' which pass the result into runModel) to
catch the thrown error and exit/return with a clear error message instead of
letting an empty object reach runModel. Ensure references to loadVariables and
runModel are used to locate the changes.

Comment on lines +78 to +87
const model = Model.load(inputFile);
const tree = GoalTree.fromModel(model, sleecEngineMapper);

const logger = initLogger(inputFile);
const fileName = path.parse(inputFile).name;
const outputPath = `output/${fileName}.prism`;

writeFile(
outputPath,
sleecTemplateEngine(tree),
sleecTemplateEngine(tree.nodes),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for sleecTemplateEngine definition
echo "=== Searching for sleecTemplateEngine definition ==="
rg -n "sleecTemplateEngine" --type=ts -C 5

echo ""
echo "=== Check package.json for module type ==="
head -30 package.json

Repository: vieirin/goal-controller

Length of output: 9337


🏁 Script executed:

#!/bin/bash
# Find GoalTree class definition to understand tree vs tree.nodes
echo "=== GoalTree class definition ==="
rg -n "class GoalTree" --type=ts -A 10

echo ""
echo "=== Check how tree.nodes is used elsewhere ==="
rg -n "tree\.nodes" --type=ts | head -20

echo ""
echo "=== Verify GoalTreeType definition ==="
rg -n "type GoalTreeType" --type=ts -A 2

Repository: vieirin/goal-controller

Length of output: 2642


Pass the full tree object to sleecTemplateEngine, not tree.nodes.

The sleecTemplateEngine function expects a GoalTreeType object and calls GoalTree.allByType(tree, 'task') internally, which requires the full tree. Currently, the code passes tree.nodes (an array) instead of the tree object. The UI implementation at packages/ui/app/api/transform/route.ts:82 correctly passes the full tree object.

🤖 Prompt for AI Agents
In `@packages/lib/src/index.ts` around lines 78 - 87, The call to
sleecTemplateEngine is being passed tree.nodes (an array) but
sleecTemplateEngine expects the full GoalTreeType so functions like
GoalTree.allByType(tree, 'task') work; update the writeFile invocation to pass
the entire tree object (the variable tree produced by GoalTree.fromModel and
used with Model.load) instead of tree.nodes so sleecTemplateEngine receives the
full structure.

@vieirin vieirin merged commit c316be1 into main Feb 11, 2026
5 checks passed
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.

1 participant