Summary
Add an internal dynamic function that computes a stable hash value for each need, populating a string hash field that links can reference in constraints such as :links: TARGET_NEED[hash=='deadbeef']. This enables stale-reference detection at build time: when the linked target's resolved content changes, its hash changes, and the constrained link no longer matches.
Two requirements drive the design:
- Late computation. The function must run after all other field resolution and dynamic functions have completed for the need, so the hash reflects the final, resolved content — not partially-resolved input. Hooking too early gives an unstable hash that depends on resolve order.
- Per-need-type include/exclude configuration. Different need types care about different fields. Users must be able to declare, per type, which fields contribute to the hash (include list) and/or which fields are excluded (exclude list). Sensible defaults should cover the common case (e.g., user-authored content fields, excluding system-derived fields like
docname / lineno / parent_needs).
The angular-bracket link-constraint syntax [field==value] is already in place — the missing piece is a built-in way to compute and assign the hash field consistently.
Related
Acceptance criteria
- A new internal dynamic function (e.g.,
[[hash()]] or equivalent registered name) is available and assignable to a string field on a need.
- Hash output is deterministic: same resolved field values produce the same hash across runs and machines.
- The function runs after every other dynamic function and field resolver for the need, so the inputs to the hash are the final values seen by
needs.json consumers.
- Configuration via
conf.py allows declaring, per need type, an include list and/or an exclude list of fields that contribute to the hash.
- Default behavior (when no per-type config is provided) is documented and covers a reasonable common subset — e.g., user-authored content fields included, system-derived/computed fields excluded.
- Field input order is stable (e.g., sorted by name) so identical content always yields the same hash regardless of dict iteration order.
- Hash algorithm and length are documented (e.g., truncated SHA-1 or similar, short enough to be human-readable in link constraints).
- Tests cover: deterministic output, late-resolution ordering (hash sees final values, not intermediate ones), per-type include/exclude configuration, default behavior, and stability across runs.
- Documentation explains how to configure include/exclude per type and how the resulting hash is consumed in link constraints.
Out of scope
- The angular-bracket link-constraint syntax
[field==value] itself — already supported in sphinx-needs.
- needs.json serialization-format changes.
- Validation tooling that surfaces stale-link diagnostics in editors (lives in consuming tools such as ubCode).
- Migration story for projects that already use a field named
hash for unrelated purposes (config-level concern, not a sphinx-needs feature).
Notes
A companion ubCode issue tracks displaying the resulting hash field via existing LSP need-detail views. Cross-link will be added once both issues are filed.
Summary
Add an internal dynamic function that computes a stable hash value for each need, populating a string
hashfield that links can reference in constraints such as:links: TARGET_NEED[hash=='deadbeef']. This enables stale-reference detection at build time: when the linked target's resolved content changes, its hash changes, and the constrained link no longer matches.Two requirements drive the design:
docname/lineno/parent_needs).The angular-bracket link-constraint syntax
[field==value]is already in place — the missing piece is a built-in way to compute and assign thehashfield consistently.Related
make_hashed_idhelper insphinx_needs/api/need.py. The new dynamic function may reuse or build on this helper.Acceptance criteria
[[hash()]]or equivalent registered name) is available and assignable to a string field on a need.needs.jsonconsumers.conf.pyallows declaring, per need type, anincludelist and/or anexcludelist of fields that contribute to the hash.Out of scope
[field==value]itself — already supported in sphinx-needs.hashfor unrelated purposes (config-level concern, not a sphinx-needs feature).Notes
A companion ubCode issue tracks displaying the resulting hash field via existing LSP need-detail views. Cross-link will be added once both issues are filed.