Skip to content

feat(ogar-vocab): ProjectRelation + ProjectChangeset + ProjectWatcher (3 promotions)#68

Merged
AdaWorldAPI merged 2 commits into
mainfrom
claude/ogar-vocab-relation-changeset-watcher
Jun 19, 2026
Merged

feat(ogar-vocab): ProjectRelation + ProjectChangeset + ProjectWatcher (3 promotions)#68
AdaWorldAPI merged 2 commits into
mainfrom
claude/ogar-vocab-relation-changeset-watcher

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

3 more promotions — closes the last project_work_item forward ref

Canonical id Redmine OpenProject
project_relation 0x0111 IssueRelation Relation
project_changeset 0x0112 Changeset Changeset
project_watcher 0x0113 Watcher Watcher

project_relation has cross-curator name divergence (Redmine IssueRelation vs OP Relation) but identical shape conceptually — directed work-item edge with from/to/relation_type/delay-or-lag. Canonical chose lag (project-scheduling standard) over Redmine's delay.

project_changeset pairs with project_repository (merged via #66): both have belongs_to :repository + belongs_to :user (committer → project_actor) plus revision/comments/commit_date.

project_watcher is the polymorphic follow-relationship both ship with belongs_to :user + belongs_to :watchable, polymorphic: true. The polymorphic target stays opaque at the canonical layer.

What

Green

ogar-vocab 41 · ogar-from-ruff 24 · ogar-from-rails 11 real-corpus (+1) · clippy clean.

Project-mgmt block (0x01XX) now 19 concepts

All project_work_item family edges (project, status, type, priority, author/assignee via project_actor, journals, relations, time_entries via billable_work_entry) point at canonical classes that exist. The ladder for OP↔Redmine is at saturation for project_work_item's immediate neighbourhood.

🤖 Generated with Claude Code

… (3 promotions)

Closes the last project_work_item forward reference (.relations) and lands
two natural neighbours that pair with already-canonical concepts:

| Canonical            | id      | Redmine        | OpenProject |
|----------------------|---------|----------------|-------------|
| project_relation     | 0x0111  | IssueRelation  | Relation    |
| project_changeset    | 0x0112  | Changeset      | Changeset   |
| project_watcher      | 0x0113  | Watcher        | Watcher     |

project_relation has cross-curator name divergence (Redmine 'IssueRelation'
vs OP 'Relation') — same shape conceptually (directed work-item edge
with from/to/type/delay-or-lag). Canonical chooses 'lag' (project-
scheduling term) over Redmine's 'delay'.

project_changeset pairs with project_repository (already canonical, #66):
both have belongs_to :repository + belongs_to :user (the committer maps
to project_actor) plus revision/comments/commit_date.

project_watcher captures the polymorphic follow-relationship both
curators ship with belongs_to :user + belongs_to :watchable, polymorphic.
The polymorphic target stays opaque at the canonical layer; the
watchable_type discriminator is the curator-local cell.

- CODEBOOK entries: 0x0111-0x0113 in the project-mgmt block.
- Resolver arms covering each curator name + PascalCase + plural forms
  (singular/plural/snake_case/PascalCase per the #66 + #67 doctrine).
- 3 canonical class functions with Language::Unknown + typed attrs +
  obvious family edges.
- Extended codebook_ids_are_domain_prefixed_and_consistent test to
  cover the 3 new concepts.
- Extended project_mgmt_batch_promotions_each_have_a_codebook_id_and_shape
  with 3 new entries.
- Extended project_mgmt_resolver_arms_collapse_curator_names with the
  alias coverage (including IssueRelation/Relation divergence).
- New real-corpus test:
  redmine_and_openproject_relation_changeset_watcher_converge — iterates
  all 6 extracted classes (Redmine IssueRelation + Changeset + Watcher,
  OP Relation + Changeset + Watcher) and asserts canonical_concept +
  canonical_id match.

ogar-vocab 41, ogar-from-ruff 24, real-corpus 11 (+1); clippy clean.

Project-mgmt block (0x01XX) is now 19 concepts.
project_work_item family edges all have canonical class targets.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36e0b144ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1558 to +1560
"issuerelation" | "issue_relation"
| "relation" | "relations"
| "project_relation" | "projectrelation"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle Redmine relation table aliases

Please add the tableized Redmine plural (issue_relations, and likely IssueRelations) to this resolver arm. ogar_codebook calls canonical_concept only once, so issue_relations currently falls through to the lexical fallback as issue_relation, which is not in CODEBOOK, and downstream consumers that resolve Rails table labels get None instead of the new project_relation id; this is the same plural-table alias case already guarded for other promoted concepts in plural_table_aliases_resolve_to_promoted_codebook_id.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Agreed and fixed directly on this branch (force-pushed ebe7631). Added "issuerelations" | "issue_relations" to the project_relation promoted arm — same pattern as #67 (wiki_pages / custom_fields). The plural_table_aliases test gained 3 new entries (issue_relations, issuerelations, IssueRelations → project_relation codebook id).

Also did a proactive audit and found the same defect class on 4 more already-merged concepts (Rails irregular pluralization the single-s lex can't reverse): time_entries, issue_statuses, issue_priorities, memberships. Those are landed in #69 (separate PR off main since they touch merged code). Thanks for the catch.

Codex flagged that ogar_codebook('issue_relations') returned None: the
promoted arm only matched the singular 'issue_relation', so the Rails-
tableized plural fell through to lexical, which drops the trailing s and
returns 'issue_relation' — the singular curator alias, NOT a canonical
concept name. canonical_concept_id then misses on the curator alias.

Same defect class as #67 (wiki_pages, custom_fields). Fix is the same:
add the plural snake_case + lowercase-PascalCase forms to the promoted
arm so they short-circuit before lexical:

  'issuerelations' | 'issue_relations'

The plural_table_aliases_resolve_to_promoted_codebook_id test gains 3
entries pinning the contract: issue_relations / issuerelations /
IssueRelations all map to project_relation codebook id.

ogar-vocab 41, clippy clean.

(Note: same defect exists on 4 other compound concepts —
time_entries / issue_statuses / issue_priorities / memberships —
fixed in a follow-up PR off main since they touch already-merged
canonical classes.)
@AdaWorldAPI AdaWorldAPI merged commit 46989c7 into main Jun 19, 2026
1 check passed
AdaWorldAPI pushed a commit that referenced this pull request Jun 19, 2026
Audit of compound-name promoted concepts revealed 4 more cases of the
plural-table-alias defect codex flagged on PR #66 (wiki_pages,
custom_fields, #67) and PR #68 (issue_relations, fixed on #68's branch):

  TimeEntry      Rails table 'time_entries'    lex 'time_entrie'  BUG
  IssueStatus    Rails table 'issue_statuses'  lex 'issue_statuse' BUG
  IssuePriority  Rails table 'issue_priorities' lex 'issue_prioritie' BUG
  Membership     plural 'memberships'           lex 'membership' BUG

All four cases: ogar_codebook(plural_form) returned None because the
single-s lexical fallback drops to a non-canonical string (Rails uses
irregular pluralization — ies→y, s→ses — that single-char drop doesn't
reverse). The promoted-arm only ran once on the full input, before lex,
so the lex'd string never got re-matched.

Fix: add the plural snake_case + lowercase-PascalCase forms to each arm:
  billable_work_entry:  + 'timeentries'   | 'time_entries'
  project_status:       + 'issuestatuses' | 'issue_statuses'
  priority:             + 'issuepriorities' | 'issue_priorities'
  project_membership:   + 'membership' | 'memberships'

Extended plural_table_aliases_resolve_to_promoted_codebook_id with 11
new entries (3 per concept where applicable) pinning each tableized
variant -> codebook id.

ogar-vocab 41 (test row count unchanged; entries inside extended);
clippy clean; all 10 real-corpus tests still pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants