{
  "issues": [
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/24",
      "id": 3676864943,
      "node_id": "I_kwDOP_5EM87bKIGv",
      "number": 24,
      "title": "Introduce `enum` field type",
      "user": {
        "login": "fdionisi",
        "id": 8927326,
        "node_id": "MDQ6VXNlcjg5MjczMjY=",
        "avatar_url": "https://avatars.githubusercontent.com/u/8927326?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/fdionisi",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 1,
      "created_at": "2025-11-29T15:39:09Z",
      "updated_at": "2025-11-30T10:27:01Z",
      "closed_at": null,
      "author_association": "NONE",
      "type": null,
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "## Motivation\n\nThe current type system supports basic primitives (string, integer, boolean, etc.) but lacks support for enumerated types. This means there's no way to constrain a field to a specific set of valid values at the schema level.\n\nIn practice, this may lead to inconsistent data entry. For something like task priority, you might end up with \"high\", \"High\", \"urgent\", \"critical\", \"CRITICAL\"—all semantically similar but practically different, making it more challenging to work with. The same applies to status fields, categories, or any domain where you have a fixed set of meaningful states.\n\n## Proposal\n\nAdd an `enum` field type that accepts a list of valid string values. The syntax could look like this:\n\n```\nschema priority_task {\n    field {\n        name = \"priority\"\n        type = \"enum\"\n        values = [\"low\", \"medium\", \"high\", \"critical\"]\n    }\n\n    field {\n        name = \"title\"\n        type = \"string\"\n        required = true\n    }\n}\n```\n\nWhen creating or updating an entity, any value not in the defined set would be rejected with a validation error. This enforcement would occur in the core library, ensuring data integrity regardless of whether you're using the CLI, a future MCP server, or another interface.\n\n## Backward compatibility\n\nThis would be purely additive—a new field type alongside the existing ones. Existing schemas and data would be completely unaffected.\n\n## Considerations\n\nA few implementation thoughts:\n\n**Case sensitivity**: should enum matching be case-sensitive? I'd argue yes—\"high\" and \"High\" should be different values if both are in the enum. This keeps things simple and predictable. If you want case-insensitive matching, define both variants explicitly.\n\n**Empty strings**: Should `\"\"` be a valid enum value if explicitly listed? Probably yes for consistency, though most use cases wouldn't need it.\n\n**Default values**: would be useful to mark one enum value as the default when creating new entities. Not essential for initial implementation, but worth considering for the design.\n\n**Validation messages**: when someone tries to write an invalid value, the error should show what values are actually valid; better DX.\n\nHappy to take a crack at implementing this if it aligns with your vision.",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/24/reactions",
        "total_count": 1,
        "+1": 1,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/23",
      "id": 3676754403,
      "node_id": "I_kwDOP_5EM87bJtHj",
      "number": 23,
      "title": "Reconsider built-in entity type design: contact information placement and schema flexibility",
      "user": {
        "login": "fdionisi",
        "id": 8927326,
        "node_id": "MDQ6VXNlcjg5MjczMjY=",
        "avatar_url": "https://avatars.githubusercontent.com/u/8927326?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/fdionisi",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {},
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 1,
      "created_at": "2025-11-29T13:18:53Z",
      "updated_at": "2025-11-30T10:43:10Z",
      "closed_at": null,
      "author_association": "NONE",
      "type": null,
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "I've been using `firm` quite a bit lately, and as an engineer turned into management, I find the system genuinely helpful and powerful. 😁\n\nHowever, I'm running into some conceptual friction with the built-in entity types that I wanted to raise.\n\nThe current design shows a `person` with contact information (email, phone), whereas the contact has none of those things. This feels backwards for real-world use cases where a single individual might have different professional contexts with distinct contact details. Which is precisely my situation: multiple individuals holding different roles across various organisations. \n\nIn this case, I would see a better fit having:\n- One `person` entity representing the individual.\n- Multiple `contact` entities for that person, each with dedicated email addresses, phone numbers, roles, and references to different organisations.\n\nGiving a concrete example, someone might be reachable at their consultancy email for project work, their university email for academic collaboration, and a personal email for informal contact.\n\nMore generally, the built-in types are opinionated and somewhat subjective. The REA model provides good grounding, but the specific field choices encode particular workflows that may not generalise well. I wonder if it would be better to leave complete freedom for entity implementation, perhaps providing the built-in schemas as opt-in/out, or to make it trivial to override them entirely.\n\n---\n\nI'm happy to prepare a PR with the proposed person/contact changes if this aligns with your vision, but before doing that, I wanted to make sure this would be valuable.\n\nEager to hear your thoughts on that and the built-in types as well.",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/23/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/22",
      "id": 3546683829,
      "node_id": "I_kwDOP_5EM87TZhm1",
      "number": 22,
      "title": "Add a way to disable interactivity for agents.",
      "user": {
        "login": "from-nibly",
        "id": 3860803,
        "node_id": "MDQ6VXNlcjM4NjA4MDM=",
        "avatar_url": "https://avatars.githubusercontent.com/u/3860803?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/from-nibly",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 1,
      "created_at": "2025-10-23T21:34:35Z",
      "updated_at": "2025-10-26T09:25:11Z",
      "closed_at": null,
      "author_association": "NONE",
      "type": null,
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "## Motivation\nA lot of agents get stuck on clis that have interactivity. It would be great if we had a ENV var or flag that could disable interactivity. \n\n## Proposal\nIf a command would normally be interactive to populate incomplete information from lack of arguments, it should print a help message\n\n## Backward compatibility\nIf it's an extra flag or env var it shouldn't\n\n## Considerations\nAgain this is mostly for agents so they can't get stuck, but this may be good for other automation like CI\n",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/22/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/21",
      "id": 3529115624,
      "node_id": "I_kwDOP_5EM87SWgfo",
      "number": 21,
      "title": "Wish: CardDAV  sync",
      "user": {
        "login": "stappersg",
        "id": 35434094,
        "node_id": "MDQ6VXNlcjM1NDM0MDk0",
        "avatar_url": "https://avatars.githubusercontent.com/u/35434094?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/stappersg",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 1,
      "created_at": "2025-10-18T19:16:37Z",
      "updated_at": "2025-10-26T20:43:17Z",
      "closed_at": null,
      "author_association": "NONE",
      "type": null,
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "## Motivation\nHave _contacts_ at a _contacts place_\n\n## Proposal\nSynchronice with CardDAV server.\nOr `export` / `import`  contacts in _CardDAV format_.\n\n## Backward compatibility\nIt is a new feature\n\n## Considerations\nHaving some `UUID` on each contact will help synchronisation.",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/21/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/17",
      "id": 3525720744,
      "node_id": "I_kwDOP_5EM87SJjqo",
      "number": 17,
      "title": "Support for XDG directories",
      "user": {
        "login": "Wilkenfeld",
        "id": 34282087,
        "node_id": "MDQ6VXNlcjM0MjgyMDg3",
        "avatar_url": "https://avatars.githubusercontent.com/u/34282087?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/Wilkenfeld",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        },
        "1": {
          "id": 9437234408,
          "node_id": "LA_kwDOP_5EM88AAAACMoDE6A",
          "url": "https://api.github.com/repos/42futures/firm/labels/question",
          "name": "question",
          "color": "d876e3",
          "default": true,
          "description": "Further information is requested"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 4,
      "created_at": "2025-10-17T11:24:13Z",
      "updated_at": "2025-10-19T14:59:48Z",
      "closed_at": null,
      "author_association": "NONE",
      "type": null,
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "Hi! I've just started to use firm and I quite enjoy it! I'm working on a similar project and this could fill a huge gap in my repo / task management segment. The first issue that I've encountered is that firm doesn't use XDG directories, which breaks some of my usual flows on MacOS / Linux. I'm willing to help with the implementation of this enhancement.\n\n## Motivation\nThe customisation and management of configs and data would be much easier (e.g. support with existing tools for version control).\n\n## Proposal\nUse XDG_DATA_DIRS and `XDG_DATA_HOME/firm` as default directories for firm data, and eventually use `XDG_{CACHE,STATE}_{HOME,DIRS}` for cache / state files.\n\n## Backward compatibility\nThis shouldn't bring backward compatibility issues, since the --workspace option would still be available.\n\n## Considerations\nNone that come up to my mind",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/17/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/16",
      "id": 3524976597,
      "node_id": "I_kwDOP_5EM87SGt_V",
      "number": 16,
      "title": "External data integrations",
      "user": {
        "login": "danielrothmann",
        "id": 19332745,
        "node_id": "MDQ6VXNlcjE5MzMyNzQ1",
        "avatar_url": "https://avatars.githubusercontent.com/u/19332745?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/danielrothmann",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        },
        "1": {
          "id": 9437234392,
          "node_id": "LA_kwDOP_5EM88AAAACMoDE2A",
          "url": "https://api.github.com/repos/42futures/firm/labels/help%20wanted",
          "name": "help wanted",
          "color": "008672",
          "default": true,
          "description": "Extra attention is needed"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 7,
      "created_at": "2025-10-17T08:07:46Z",
      "updated_at": "2025-10-20T20:30:59Z",
      "closed_at": null,
      "author_association": "MEMBER",
      "type": {
        "id": 12340087,
        "node_id": "IT_kwDOA95Nmc4AvEt3",
        "name": "Feature",
        "description": "A request, idea, or new functionality",
        "color": "blue",
        "created_at": "2024-02-04T17:19:48Z",
        "updated_at": "2024-10-08T16:48:31Z",
        "is_enabled": true
      },
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "## Motivation\nFirm doesn't exist in isolation. While some aspects may be managed with firm over other solutions, a modern business will still have external tools that they use - accounting tools, CRM, wordpress, etc.\n\nWe need a way to bring external information into the firm graph so it can be queried in a holistic context.\n\n## Proposal\nNot exactly sure what would be the most ergonomic way of doing this.\n\n### Idea: `External` block\nMimics in some aspects the Terraform data block, except it would run arbitrary scripts to get the data.\n\n```\nexternal social_integration {\n    script = path\"./get_social_data.py\"\n    arguments = \"--id some_id\"\n}\n```\n\nAn external block could define a script (Python? JS?) which outputs an entity. When the graph is built, the script is evaluated to produce the entity. Other entities might then be able to reference that external:\n\n```\nperson from_social {\n   name = external.social_integration.name\n}\n```\n\n### Idea: `Expand` block\nA block which expands into a DSL entity.\n\n```\nexpand social_integration {\n    script = path\"./get_social_data.py\"\n    arguments = \"--id some_id\"\n}\n```\n\nAn external block could also define a script which outputs an entity. The difference being that this gets resolved when the DSL is parsed, generating a new DSL entity block next to the expand block. We would need some way of tracking where the DSL came from, so it's not expanded twice and could potentially be updated if we have changes.\n\n```\nperson from_social {\n   name = \"Expanded name\"\n   expanded_from = expand.social_integration\n}\n```\n\n## Backward compatibility\nI reckon this can be introduced as a new language type, keeping entities / schemas intact.\n\n## Considerations\nWhat happens when an external integration fails to run or produce the expected entity? How to test? Fail early or best effort?\n",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/16/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/12",
      "id": 3521934103,
      "node_id": "I_kwDOP_5EM87R7HMX",
      "number": 12,
      "title": "MCP integration",
      "user": {
        "login": "danielrothmann",
        "id": 19332745,
        "node_id": "MDQ6VXNlcjE5MzMyNzQ1",
        "avatar_url": "https://avatars.githubusercontent.com/u/19332745?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/danielrothmann",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 0,
      "created_at": "2025-10-16T13:37:08Z",
      "updated_at": "2025-10-16T13:37:17Z",
      "closed_at": null,
      "author_association": "MEMBER",
      "type": {
        "id": 12340087,
        "node_id": "IT_kwDOA95Nmc4AvEt3",
        "name": "Feature",
        "description": "A request, idea, or new functionality",
        "color": "blue",
        "created_at": "2024-02-04T17:19:48Z",
        "updated_at": "2024-10-08T16:48:31Z",
        "is_enabled": true
      },
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "## Motivation\nFirm works well as a plain-text business infrastructure layer for LLMs. While a chatbot can interact with Firm DSL directly and use the CLI through tool calls, adding MCP support would open the possibility of using a chatbot as \"notetaker\" using voice commands in the Claude app, etc. \n\nThis is a workflow I'd like to experiment with supporting.\n\n## Proposal\nAn MCP server that operates on a given firm workspace. It would expose actions for LLMs to add, edit and query entities. The official [Rust MCP SDK](https://github.com/modelcontextprotocol/rust-sdk) could potentially be used to scaffold this. \n\n## Backward compatibility\nWouldn't break compatibility, I think.\n\n## Considerations\nBidirectional DSL edit is not yet supported. That would be required in order to let LLMs edit existing entities using the Firm tooling, unless they are given access to the DSL directly.\n",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/12/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/11",
      "id": 3521914259,
      "node_id": "I_kwDOP_5EM87R7CWT",
      "number": 11,
      "title": "Language server",
      "user": {
        "login": "danielrothmann",
        "id": 19332745,
        "node_id": "MDQ6VXNlcjE5MzMyNzQ1",
        "avatar_url": "https://avatars.githubusercontent.com/u/19332745?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/danielrothmann",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 9,
      "created_at": "2025-10-16T13:31:56Z",
      "updated_at": "2025-11-11T07:24:32Z",
      "closed_at": null,
      "author_association": "MEMBER",
      "type": {
        "id": 12340087,
        "node_id": "IT_kwDOA95Nmc4AvEt3",
        "name": "Feature",
        "description": "A request, idea, or new functionality",
        "color": "blue",
        "created_at": "2024-02-04T17:19:48Z",
        "updated_at": "2024-10-08T16:48:31Z",
        "is_enabled": true
      },
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "## Motivation\nEven though editing Firm DSL is simple enough, there are UX problems around schema discovery and not being able to check if the config is valid until the workspace is built.\n\nIt would be a nice addition beyond basic syntax highlighting to be able to provide hints around possible field names, default values and error highlighting. This requires implementing LSP for firm.\n\n## Proposal\nAdd firm_lsp to the Rust workspace. We could leverage [lap-types](https://github.com/gluon-lang/lsp-types) or [tower-lsp](https://github.com/ebkalderon/tower-lsp) for scaffolding the setup.\n\nIdeally, the firm LSP eventually supports:\n- Autocompletion of entity types, field names and references\n- Error checking for invalid syntax, field values, references\n- Go to definition for references and schemas\n\nA first scope could be autocomplete for field names (in the entity schema) and basic error checking.\n\n## Backward compatibility\nNeed to get into the work to understand if implementation would require breaking changes to existing data models.\n",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/11/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/10",
      "id": 3521417098,
      "node_id": "I_kwDOP_5EM87R5I-K",
      "number": 10,
      "title": "Query engine",
      "user": {
        "login": "danielrothmann",
        "id": 19332745,
        "node_id": "MDQ6VXNlcjE5MzMyNzQ1",
        "avatar_url": "https://avatars.githubusercontent.com/u/19332745?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/danielrothmann",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        },
        "1": {
          "id": 9437234392,
          "node_id": "LA_kwDOP_5EM88AAAACMoDE2A",
          "url": "https://api.github.com/repos/42futures/firm/labels/help%20wanted",
          "name": "help wanted",
          "color": "008672",
          "default": true,
          "description": "Extra attention is needed"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 1,
      "created_at": "2025-10-16T11:14:19Z",
      "updated_at": "2025-11-29T11:26:48Z",
      "closed_at": null,
      "author_association": "MEMBER",
      "type": {
        "id": 12340087,
        "node_id": "IT_kwDOA95Nmc4AvEt3",
        "name": "Feature",
        "description": "A request, idea, or new functionality",
        "color": "blue",
        "created_at": "2024-02-04T17:19:48Z",
        "updated_at": "2024-10-08T16:48:31Z",
        "is_enabled": true
      },
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "## Motivation\nThe library and CLI currently lets you do \"first-order\" queries on the workspace. This means getting an entity, listing entities of type or getting entities related to a specific entity.\n\nFrom there, you can script your way to more sophisticated answers. But it would be better if the library supported this.\n\nConsider these queries:\n- Find all tasks that are not completed yet, ordered by due data\n- Find the 10 most recent contacts which do not have any interactions associated with them\n- Find recent opportunities that don't have a status of \"Closed Won / Closed Lost\"\n\nIt would be ideal to support these kinds of queries both in the library and CLI.\n\n## Proposal\nTo do this we need a \"query engine\". My mental model of this is a sequence of commands - select, filter, expand, order, get. Something like that. Is there something well-established we could lean on?\n\nI could imagine queuing up these commands like so:\n- Select tasks\n- Filter by is_completed equals false\n- Order by due_date ascending/descending\n\nOr:\n- Select opportunities\n- Filter by status contains \"Closed\"\n- Order by created_at ascending/descending\n\nOr:\n- Select contacts\n- Filter by num (related filter by type equals interaction) equals 0 (how to do this?)\n- Order by created_at ascending/descending\n- Limit 10\n\n## Backward compatibility\nShould be an addition, not a change. It would introduce a new API which should be backward compatible.\n\n## Considerations\nHow could this be achieved without getting overly complex or reinventing the wheel?",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/10/reactions",
        "total_count": 1,
        "+1": 1,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    },
    {
      "url": "https://api.github.com/repos/42futures/firm/issues/7",
      "id": 3517127263,
      "node_id": "I_kwDOP_5EM87Roxpf",
      "number": 7,
      "title": "Third field category “internal”",
      "user": {
        "login": "MHohenberg",
        "id": 239332,
        "node_id": "MDQ6VXNlcjIzOTMzMg==",
        "avatar_url": "https://avatars.githubusercontent.com/u/239332?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/MHohenberg",
        "type": "User",
        "user_view_type": "public",
        "site_admin": false
      },
      "labels": {
        "0": {
          "id": 9437234377,
          "node_id": "LA_kwDOP_5EM88AAAACMoDEyQ",
          "url": "https://api.github.com/repos/42futures/firm/labels/enhancement",
          "name": "enhancement",
          "color": "a2eeef",
          "default": true,
          "description": "New feature or request"
        }
      },
      "state": "open",
      "locked": false,
      "assignee": null,
      "assignees": {},
      "milestone": null,
      "comments": 1,
      "created_at": "2025-10-15T09:16:37Z",
      "updated_at": "2025-10-15T10:00:35Z",
      "closed_at": null,
      "author_association": "NONE",
      "type": {
        "id": 12340087,
        "node_id": "IT_kwDOA95Nmc4AvEt3",
        "name": "Feature",
        "description": "A request, idea, or new functionality",
        "color": "blue",
        "created_at": "2024-02-04T17:19:48Z",
        "updated_at": "2024-10-08T16:48:31Z",
        "is_enabled": true
      },
      "active_lock_reason": null,
      "sub_issues_summary": {
        "total": 0,
        "completed": 0,
        "percent_completed": 0
      },
      "issue_dependencies_summary": {
        "blocked_by": 0,
        "total_blocked_by": 0,
        "blocking": 0,
        "total_blocking": 0
      },
      "body": "firm add currently treats fields as required or optional. Some fields (e.g., created, updated) are system-managed and shouldn’t be user-editable during interactive entry. Introduce a third category - internal - to mark fields that are set by Firm (or derived) and hidden or read-only in interactive flows.\n\n## Motivation\n\n* Reduce friction: users shouldn’t have to tab through non-editable metadata just to reach the single optional field they actually want.\n* Prevent accidental edits: timestamps and integrity fields shouldn’t be user-supplied.\n* Clearer intent: schemas can express “who owns this value” (user vs. system) rather than just “is it required.”\n\n## Proposal\n\n### 1) Add per-field properties:\n\n    field \"created\" {\n      type       = \"datetime\"\n      mode       = \"internal\"    # \"required\" | \"optional\" | \"internal\"\n      compute    = \"now()\"       # optional: function or template\n      editable   = false         # default for internal\n      visible    = \"hidden\"      # \"visible\" | \"hidden\"\n    }\n\n    field \"updated\" {\n      type       = \"datetime\"\n      mode       = \"internal\"\n      compute    = \"now()\"       # auto-set on create; update hook bumps this\n      on_update  = \"now()\"       # recompute on mutation\n    }\n\n    field \"name\" {\n      type       = \"string\"\n      mode       = \"required\"    # prompted\n    }\n\n    field \"notes\" {\n      type       = \"string\"\n      mode       = \"optional\"    # prompted after requireds\n    }\n\nSome notes:\n\n* mode=internal implies editable=false in interactive prompts and CLI flags.\n* compute/on_update let Firm populate values automatically.\n* visible=hidden keeps internal fields out of firm add prompts and short views, while still persisting in the file.\n\n### 2) CLI Behaviour (firm add)\n\nPrompt order: required → optional. Internal fields are skipped.\n\nFlags:\n* --show-internal (view-only) prints computed/internal fields at the end.\n* --no-optional jumps straight to save after requireds.\n\nValidation:\n* If a user provides an internal field via --field created=..., reject with a clear error: “Field ‘created’ is internal and cannot be set interactively.”\n\nHooks:\n* On create, evaluate compute.\n* On update/mutate, evaluate on_update for relevant fields.\n\n## Backward compatibility\n\n* existing schemas default to mode=\"optiona\" when no mode is specified, preserving behaviour\n\n## Risks / Considerations\n\n* Need a deterministic set of compute functions (e.g. now(), uuid(), hash(fields...)) to avoid non-reproducible builds\n\n",
      "closed_by": null,
      "reactions": {
        "url": "https://api.github.com/repos/42futures/firm/issues/7/reactions",
        "total_count": 0,
        "+1": 0,
        "-1": 0,
        "laugh": 0,
        "hooray": 0,
        "confused": 0,
        "heart": 0,
        "rocket": 0,
        "eyes": 0
      },
      "performed_via_github_app": null,
      "state_reason": null,
      "linked_prs": []
    }
  ],
  "pulls": [],
  "discussions": [],
  "details": {
    "id": 1073628211,
    "node_id": "R_kgDOP_5EMw",
    "name": "firm",
    "full_name": "42futures/firm",
    "private": false,
    "owner": {
      "login": "42futures",
      "id": 64900505,
      "node_id": "MDEyOk9yZ2FuaXphdGlvbjY0OTAwNTA1",
      "avatar_url": "https://avatars.githubusercontent.com/u/64900505?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/42futures",
      "type": "Organization",
      "user_view_type": "public",
      "site_admin": false
    },
    "description": "A text-based work management system for technologists.",
    "fork": false,
    "url": "https://api.github.com/repos/42futures/firm",
    "created_at": "2025-10-10T11:42:37Z",
    "updated_at": "2025-12-03T18:33:44Z",
    "pushed_at": "2025-11-07T13:22:19Z",
    "homepage": "",
    "size": 271,
    "stargazers_count": 670,
    "watchers_count": 670,
    "language": "Rust",
    "has_issues": true,
    "has_projects": false,
    "has_downloads": true,
    "has_wiki": false,
    "has_pages": false,
    "has_discussions": false,
    "forks_count": 35,
    "archived": false,
    "disabled": false,
    "open_issues_count": 10,
    "license": {
      "key": "agpl-3.0",
      "name": "GNU Affero General Public License v3.0",
      "spdx_id": "AGPL-3.0",
      "url": "https://api.github.com/licenses/agpl-3.0",
      "node_id": "MDc6TGljZW5zZTE="
    },
    "allow_forking": true,
    "is_template": false,
    "web_commit_signoff_required": false,
    "topics": {
      "0": "business",
      "1": "cli",
      "2": "dsl",
      "3": "firm",
      "4": "graph",
      "5": "work"
    },
    "visibility": "public",
    "forks": 35,
    "open_issues": 10,
    "watchers": 670,
    "default_branch": "main",
    "permissions": {
      "admin": false,
      "maintain": false,
      "push": false,
      "triage": false,
      "pull": true
    },
    "temp_clone_token": "",
    "custom_properties": {},
    "organization": {
      "login": "42futures",
      "id": 64900505,
      "node_id": "MDEyOk9yZ2FuaXphdGlvbjY0OTAwNTA1",
      "avatar_url": "https://avatars.githubusercontent.com/u/64900505?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/42futures",
      "type": "Organization",
      "user_view_type": "public",
      "site_admin": false
    },
    "network_count": 35,
    "subscribers_count": 6
  },
  "lastFetched": 1765133617966
}