Files
OpenSpec/docs-lab/reference
e0a2f00f95 fix(metadata): stop reporting x- extension keys in .openspec.yaml as unknown (#2063)
* feat(metadata): x- extension keys in .openspec.yaml are not reported as unknown

Teams that keep tool data beside a change (a tracker id, a goal id, an owner) had no
place for it: since 1.14.0 every unrecognized top-level key is a validation warning,
so validate --strict fails. Keys that start with x- are now extension metadata, as in
OpenAPI: never read, never written, never reported. Every other unknown key is still
reported, and the warning now names the x- prefix as the way to keep such data.

Refs #657

* fix(metadata): accept any x- name, and say so when a key just misses

The extension grammar was narrower than the contract above it. The doc
comment said "the prefix is the whole contract, as in OpenAPI's x-
extensions", but the pattern was `/^x-[a-z0-9][a-z0-9_-]*$/`, so the
spellings teams actually write were still reported:

  x-jiraKey    warned     x-a-       accepted
  x-trackerID  warned     x-foo_bar  accepted
  x-Owner      warned

`x-jiraKey` is the obvious key for the tracker case in #657, and it failed
`validate --strict` while a trailing-hyphen name passed. Worse, the new
remediation sentence told that user to "prefix a key with x-" - which they
had done - so the warning became a dead end with no way out from the
message alone. That is a worse experience than the warning this PR removes.

The name after the prefix is now the owner's business: `/^x-\S+$/`. Empty
and whitespace-carrying names stay rejected, the latter because whitespace
would let a key forge a second line in the warning output.

The prefix itself stays lowercase, keeping this PR's deliberate
`X-goal -> false`. That is the one near-miss a user cannot diagnose from
the old message, so the message now names both conditions it does not
cover, and only when a reported key actually starts with the prefix.

Also pinned the round-trip, which was the risk worth checking: archive
moves `.openspec.yaml` rather than rewriting it, and `writeChangeMetadata`
only ever writes a new file, so extension keys survive today. But
`ChangeMetadataSchema` is not passthrough and strips them, so the first
read-modify-write caller would silently delete data a team put there.
A test pins the stripping and the writer's doc comment now says to merge
`x-` keys back from the raw YAML.

Documented the namespace in docs/cli.md next to the other metadata keys,
since `x-* (yours)` in a directory-tree comment could not carry the rule,
and flipped the changeset to `patch` to match the `fix` retitle and the
#2020/#2028 precedent.

Verified end to end: a change with `x-jiraKey` and `x-Owner` passes
`validate --strict`, while `X-Tracker` beside a `taskss` typo reports both
and explains the prefix. 6413 tests pass; lint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(metadata): merge the stacked writeChangeMetadata JSDoc and trim extension-key comments

- writeChangeMetadata had two consecutive /** */ blocks, orphaning the first;
  merge them into one that keeps the params and the x- key stripping caveat.
- Trim the EXTENSION_KEY_PATTERN comment to the contract, dropping the
  history of the earlier grammar and the inaccurate claim that whitespace
  could forge a warning line (reported keys are already sanitized).
- Tighten the changeset to the user-facing behavior.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(archive): keep x- extension keys byte-for-byte through archive

Archive is the only path that touches an existing .openspec.yaml; it moves
the file rather than rewriting it. Pin that, and that archive --json emits no
unknown-key warning for x- keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(metadata): document x- extension keys in the docs-lab reference

The canonical change-metadata reference still said every unknown top-level
key is reported and fails --strict. Add an Extension keys section (name
rule, ignored value, top-level scope, strict result, archive) and point
the Validation bullets at it.

Pin each documented claim where it is enforced, not only in the helper:
the validator passes --strict on x- keys with any YAML value, still fails
on X-Tracker, a bare x- and "x- tracker", and loadChangeContext (status,
instructions) returns no warning. All three tests fail on main's code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: beast-infra <hi@fbeeg.io>
Co-authored-by: Clay Good <hi@claygood.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-14 23:27:55 +00:00
..