Change BaseFrontMatter URN field from string to *urn.URN type#206
Change BaseFrontMatter URN field from string to *urn.URN type#206
Conversation
- Added github.com/leodido/go-urn dependency - Created pkg/codingcontext/urn package with YAML/JSON marshaling support - Changed BaseFrontMatter.URN field type from string to *urn.URN - Changed YAML/JSON tags from "id" to "urn" - Updated all test files to use new URN type with helper functions - Updated result.go to handle URN pointer conversion to string Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
- Updated URN comparisons to use .String() method - Changed YAML field from "id" to "urn" in test data - Fixed compilation errors in context_test.go Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
- Improved error message in URN UnmarshalYAML - Fixed nil URN handling in MCPServers to skip entries instead of using empty key - Removed unused import Co-authored-by: alexec <1142830+alexec@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request changes the BaseFrontMatter.URN field from a string type to a pointer to a new *urn.URN wrapper type, and renames the YAML/JSON tag from "id" to "urn". The change addresses feedback from PR #205 to adopt a more formal URN-based resource naming scheme.
Changes:
- Introduced a new
pkg/codingcontext/urnpackage that wrapsgithub.com/leodido/go-urnwith custom YAML/JSON marshaling support - Changed
BaseFrontMatter.URNfield type fromstringto*urn.URNand updated the tag from "id" to "urn" - Updated
MCPServers()method to skip rules with nil URNs instead of using empty strings as keys - Updated all existing tests to use
mustParseURN*()helper functions and URN comparison methods
Reviewed changes
Copilot reviewed 9 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/codingcontext/urn/urn.go | New URN wrapper package with YAML/JSON marshaling, nil-safe String() and Equal() methods |
| pkg/codingcontext/markdown/frontmatter.go | Changed URN field type from string to *urn.URN, updated tag from "id" to "urn" |
| pkg/codingcontext/result.go | Updated MCPServers() to skip rules with nil URNs and use String() for map keys |
| pkg/codingcontext/result_test.go | Added mustParseURNResult() helper and updated tests to use URN type |
| pkg/codingcontext/markdown/frontmatter_task_test.go | Added mustParseURN() helper and updated fixtures from "id:" to "urn:" |
| pkg/codingcontext/markdown/frontmatter_rule_test.go | Added mustParseURNRule() helper and updated fixtures from "id:" to "urn:" |
| pkg/codingcontext/markdown/frontmatter_command_test.go | Added mustParseURNCommand() helper and updated fixtures from "id:" to "urn:" |
| pkg/codingcontext/context_test.go | Updated integration tests to check for nil URNs and use String() method |
| go.mod | Added dependency on github.com/leodido/go-urn v1.4.0 |
| go.sum | Added checksums for the new dependency |
| // URN is an optional unique identifier for the prompt in URN format (e.g. urn:agents:task:<name>) | ||
| // Automatically inferred from filename if not specified in frontmatter | ||
| URN string `yaml:"id,omitempty" json:"id,omitempty"` | ||
| // If not specified in frontmatter, this field remains empty and must be set explicitly by tooling if required. | ||
| URN *urn.URN `yaml:"urn,omitempty" json:"urn,omitempty"` |
There was a problem hiding this comment.
The documentation in docs/reference/file-formats.md references "id" as the field name in multiple places (lines 32, 40, 486, 492, 652, 658), but this PR changes the field from "id" to "urn". The documentation should be updated to reflect the new field name and clarify that it expects URN format (e.g., "urn:agents:task:task-name").
| // URN is an optional unique identifier for the prompt in URN format (e.g. urn:agents:task:<name>) | ||
| // Automatically inferred from filename if not specified in frontmatter | ||
| URN string `yaml:"id,omitempty" json:"id,omitempty"` | ||
| // If not specified in frontmatter, this field remains empty and must be set explicitly by tooling if required. | ||
| URN *urn.URN `yaml:"urn,omitempty" json:"urn,omitempty"` |
There was a problem hiding this comment.
The example files in the examples directory still use the old "id:" field name instead of "urn:". These files should be updated to use the new field name and URN format. Affected files include:
- examples/agents/commands/example-command-with-standard-fields.md (line 2)
- examples/agents/rules/example-rule-with-standard-fields.md (line 2)
- examples/agents/tasks/example-with-standard-fields.md (line 2)
Additionally, the content and comments in these example files reference "id" as the field name, which should be updated to "urn".
| // Skip rules without URN | ||
| if rule.FrontMatter.URN == nil { | ||
| continue | ||
| } |
There was a problem hiding this comment.
The implementation silently skips rules without URN. Consider logging when a rule with MCP server configuration is skipped due to missing URN, as this could be unexpected behavior and difficult to debug. This would help developers understand why their MCP server configuration isn't being included.
| package urn | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| exturn "github.com/leodido/go-urn" | ||
| ) | ||
|
|
||
| // URN is a wrapper around github.com/leodido/go-urn.URN that adds YAML marshaling support | ||
| type URN struct { | ||
| *exturn.URN | ||
| } | ||
|
|
||
| // Parse wraps the external URN Parse function | ||
| func Parse(u []byte, options ...exturn.Option) (*URN, bool) { | ||
| urn, ok := exturn.Parse(u, options...) | ||
| if !ok { | ||
| return nil, false | ||
| } | ||
| return &URN{URN: urn}, true | ||
| } | ||
|
|
||
| // MarshalYAML implements yaml.Marshaler | ||
| func (u *URN) MarshalYAML() (interface{}, error) { | ||
| if u == nil || u.URN == nil { | ||
| return nil, nil | ||
| } | ||
| return u.URN.String(), nil | ||
| } | ||
|
|
||
| // UnmarshalYAML implements yaml.Unmarshaler | ||
| func (u *URN) UnmarshalYAML(unmarshal func(interface{}) error) error { | ||
| var s string | ||
| if err := unmarshal(&s); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| parsed, ok := exturn.Parse([]byte(s)) | ||
| if !ok { | ||
| return fmt.Errorf("invalid URN format: %q", s) | ||
| } | ||
|
|
||
| u.URN = parsed | ||
| return nil | ||
| } | ||
|
|
||
| // MarshalJSON implements json.Marshaler | ||
| func (u *URN) MarshalJSON() ([]byte, error) { | ||
| if u == nil || u.URN == nil { | ||
| return []byte("null"), nil | ||
| } | ||
| return u.URN.MarshalJSON() | ||
| } | ||
|
|
||
| // UnmarshalJSON implements json.Unmarshaler | ||
| func (u *URN) UnmarshalJSON(data []byte) error { | ||
| var urn exturn.URN | ||
| if err := urn.UnmarshalJSON(data); err != nil { | ||
| return err | ||
| } | ||
| u.URN = &urn | ||
| return nil | ||
| } | ||
|
|
||
| // String returns the URN string representation | ||
| func (u *URN) String() string { | ||
| if u == nil || u.URN == nil { | ||
| return "" | ||
| } | ||
| return u.URN.String() | ||
| } | ||
|
|
||
| // Equal compares two URNs for equality | ||
| func (u *URN) Equal(other *URN) bool { | ||
| if u == nil && other == nil { | ||
| return true | ||
| } | ||
| if u == nil || other == nil { | ||
| return false | ||
| } | ||
| if u.URN == nil && other.URN == nil { | ||
| return true | ||
| } | ||
| if u.URN == nil || other.URN == nil { | ||
| return false | ||
| } | ||
| return u.URN.Equal(other.URN) | ||
| } |
There was a problem hiding this comment.
The new URN package lacks test coverage. Given that this package introduces a new wrapper type with custom marshaling/unmarshaling logic and nil handling, it should have comprehensive unit tests to verify:
- Parse() function with valid and invalid URN strings
- MarshalYAML/UnmarshalYAML with nil values and valid URNs
- MarshalJSON/UnmarshalJSON with nil values and valid URNs
- String() method with nil receiver and nil internal URN
- Equal() method with various combinations of nil values
The project standard is to use table-driven tests with descriptive scenario names (as seen in other test files), and the guideline specifies maintaining >80% test coverage.
Addresses feedback from #205 to change the URN field name from "id" to "urn" and type from
stringto*urn.URN.Changes
Type system changes:
BaseFrontMatter.URNfield type fromstringto*urn.URNwith updated YAML/JSON tags (id→urn)pkg/codingcontext/urnpackage wrappinggithub.com/leodido/go-urnwith YAML marshaling supportString(),Equal(), andParse()methods to URN wrapper typeCode updates:
MCPServers()to skip nil URNs instead of using empty string keysmustParseURN()helpers and URN comparison via.Equal()or.String()id: urn:...tourn: urn:...Example:
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.