Skip to content

Add internal dynamic function to compute per-need hash #1701

@ubmarco

Description

@ubmarco

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:

  1. 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.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions