Summary
351 of larpingapp's 389 @spec annotations point at a path that does not exist. They were left behind by opsx-archive: archiving moves openspec/changes/<c>/… to openspec/changes/archive/<c>/…, and nothing repoints the annotations that referenced the old location.
Nothing reports this. gate-16 counts the presence of a @spec tag, never whether it resolves — so larpingapp reports perfect spec coverage while ~90% of its tags are dangling.
This is the @spec counterpart of #301, which found the same thing for 20 @e2e tags.
Measured
Resolver over every tracked .php/.ts/.js/.vue, with a positive control built in (it refuses to report unless at least one anchor resolves):
RESOLVING anchors : {'spec': 38, 'e2e': 114} <- positive control, the matcher works
DANGLING anchors : 374 ({'spec': 351, 'e2e': 23})
Top dangling targets:
| count |
target |
| 235 |
openspec/changes/retrofit-2026-05-24-annotate-larpingapp/tasks.md |
| 25 |
openspec/changes/event-checkin-roster/specs/event-checkin-roster/spec.md |
| 19 |
openspec/changes/skill-requirement-enforcement/specs/skill-requirement-enforcement/spec.md |
| 17 |
openspec/changes/skill-tree-visualization/specs/skill-tree-visualization/spec.md |
| 15 |
openspec/changes/retrofit-2026-05-25-larpingapp-frontend/tasks.md |
| 12 |
openspec/changes/event-runsheet-export/specs/pdf-export/spec.md |
Worst files: CharactersController.php 38, CharacterService.php 30, RegisterObjectFetcher.php 29, DeepLinkRegistrationListener.php 23, SettingsController.php 21.
Confirmation that archiving is the cause — for skill-requirement-enforcement:
openspec/changes/skill-requirement-enforcement/specs/…/spec.md -> does NOT exist (19 anchors point here)
openspec/changes/archive/skill-requirement-enforcement/specs/…/spec.md -> exists
openspec/specs/skill-requirement-enforcement/spec.md -> exists (the correct target)
There are 32 archived changes in this repo.
gate-16 measurement, for the record
Re-measured with an explicit base per ConductionNL/.github#361, on 526bbc5f (= origin/development = the headSha of full-scope run 31490125155):
HYDRA_GATE_BASE_REF |
# count= |
origin/development |
0 |
origin/beta |
0 |
empty tree 4b825dc… (full tree) |
0 |
--mode report (not diff-scoped) |
uncovered_count: 0 |
Proven alive with a committed planted true positive (IdListNormaliser::countProbeIds(), no @spec) → # count=1 at all three bases, naming the plant; reverted. So gate-16's PASS here is honest about tag presence — and that is a different claim from "traceability works".
Suggested resolution
Do not bulk-rewrite changes/ → changes/archive/. The project rule is that @spec targets the canonical openspec/specs/<capability>/spec.md, never a change directory — and the largest block (235) points at a tasks.md#task-N, which is the wrong kind of artifact regardless of the path.
- Repoint to the canonical
openspec/specs/… spec, per file, verifying the target section actually describes what the annotated method does.
- The 235
retrofit-…/tasks.md#task-N anchors need a decision: they encode "this method was annotated during the retrofit", not a requirement, and arguably should be replaced by a real spec reference or dropped.
- Worth raising in
ConductionNL/.github: opsx-archive should either repoint annotations or refuse to archive while annotations reference the change path. A git mv of a spec tree is a silent mass-dangling event and no gate in the suite reports it.
Summary
351 of larpingapp's 389
@specannotations point at a path that does not exist. They were left behind byopsx-archive: archiving movesopenspec/changes/<c>/…toopenspec/changes/archive/<c>/…, and nothing repoints the annotations that referenced the old location.Nothing reports this. gate-16 counts the presence of a
@spectag, never whether it resolves — so larpingapp reports perfect spec coverage while ~90% of its tags are dangling.This is the
@speccounterpart of #301, which found the same thing for 20@e2etags.Measured
Resolver over every tracked
.php/.ts/.js/.vue, with a positive control built in (it refuses to report unless at least one anchor resolves):Top dangling targets:
openspec/changes/retrofit-2026-05-24-annotate-larpingapp/tasks.mdopenspec/changes/event-checkin-roster/specs/event-checkin-roster/spec.mdopenspec/changes/skill-requirement-enforcement/specs/skill-requirement-enforcement/spec.mdopenspec/changes/skill-tree-visualization/specs/skill-tree-visualization/spec.mdopenspec/changes/retrofit-2026-05-25-larpingapp-frontend/tasks.mdopenspec/changes/event-runsheet-export/specs/pdf-export/spec.mdWorst files:
CharactersController.php38,CharacterService.php30,RegisterObjectFetcher.php29,DeepLinkRegistrationListener.php23,SettingsController.php21.Confirmation that archiving is the cause — for
skill-requirement-enforcement:There are 32 archived changes in this repo.
gate-16 measurement, for the record
Re-measured with an explicit base per
ConductionNL/.github#361, on526bbc5f(=origin/development= theheadShaof full-scope run31490125155):HYDRA_GATE_BASE_REF# count=origin/developmentorigin/beta4b825dc…(full tree)--mode report(not diff-scoped)uncovered_count: 0Proven alive with a committed planted true positive (
IdListNormaliser::countProbeIds(), no@spec) →# count=1at all three bases, naming the plant; reverted. So gate-16's PASS here is honest about tag presence — and that is a different claim from "traceability works".Suggested resolution
Do not bulk-rewrite
changes/→changes/archive/. The project rule is that@spectargets the canonicalopenspec/specs/<capability>/spec.md, never a change directory — and the largest block (235) points at atasks.md#task-N, which is the wrong kind of artifact regardless of the path.openspec/specs/…spec, per file, verifying the target section actually describes what the annotated method does.retrofit-…/tasks.md#task-Nanchors need a decision: they encode "this method was annotated during the retrofit", not a requirement, and arguably should be replaced by a real spec reference or dropped.ConductionNL/.github:opsx-archiveshould either repoint annotations or refuse to archive while annotations reference the change path. Agit mvof a spec tree is a silent mass-dangling event and no gate in the suite reports it.