Skip to content

Reference implementation hashes inputs the spec says to reject #35

Description

@aeoess

compute_action_ref and compute_action_ref_v2 in plugins/agt_evidence_anchor/action_ref.py hash inputs that docs/spec/action-ref.md requires a verifier to reject before any digest comparison.

Context for why I have vectors to compare against: the APS SDK ships a cross-ecosystem correlation helper whose stated purpose is to reproduce this derivation byte for byte, with 13 published vectors at conformance/action-ref-v1/vectors.json. Over inputs both profiles accept, the digests agree exactly. The mismatch is in profile enforcement rather than in the hash.

The five timestamp cases. The Domain paragraph requires OUT_OF_PROFILE_DOMAIN and a stop before any digest comparison for malformed timestamp grammar. Both functions hash all five of these and return a normal-looking reference:

  • 2026-05-24t10:30:00.000Z lowercase separator
  • 2026-05-24T10:30:00.000z lowercase suffix
  • 2026-05-24T10:30:00.000+00:00 numeric offset
  • 2026-05-24T10:30:00Z second precision
  • 2026-05-24T10:30:00.000000Z six fractional digits

Worth knowing how this gets reached. The default idiom in Python produces a form the spec rejects:

datetime.now(timezone.utc).isoformat()          2026-07-29T21:49:13.312610+00:00
isoformat(timespec="milliseconds")              2026-07-29T21:49:13.312+00:00
format_timestamp(datetime.now(timezone.utc))    2026-07-29T21:49:13.312Z

Only the third is conformant. The second still carries a numeric offset. So an implementer reaching for the language default rather than your format_timestamp helper produces two of the five rejected shapes at once, and the function hashes it.

Two other cases that may need clarification.

av-003 carries a scope of scope:звіт/📄, which is non-ASCII and includes an astral-plane character. The Domain paragraph excludes non-ASCII field values and surrogate-pair scope strings by name. The function hashed it and returned my expected digest.

av-007 carries an empty scope. The field table says scope is a free-form non-empty string, and the following line says to pass "" if not applicable. That reads as a wording conflict rather than a real divergence, but the function hashes it either way.

So the written profile and the copyable functions currently accept different input domains. Two fixes look clean from outside:

  1. Enforce the profile inside both functions before hashing.
  2. Mark the functions as derivation-only and ship a profile validator that the examples call first.

I have not tried to determine whether any caller validates first, since the documented consumers are external.

Happy to attach the full 13-vector result matrix if that is useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions