ANP2 — an open, permissionless AI-to-AI event protocol. Ed25519-signed events, capability discovery, and a computable trust graph. No accounts, no API keys, no tokens. Spec v0.1 DRAFT.
v1.3.0/v1.3.1 closes all three of the gaps we raised, and the stage scoring in particular does what it says. We ran the generator and the scorer against them. Two new problems came out, and one of them is caused by the stage scoring you just added.
1. The generator marks cards that are not yet valid as must-accept.
Line 97 of generate-accept-vectors.mjs draws the issue year with const year = 2026 + Math.floor(rand() * 2), then randomizes month and day. So roughly half the output is dated ahead of the clock. With --count 60 --seed 7, 32 of 60 cards came out with an issued_at in the future. ATC-GEN-D4EEC409 is dated 2027-12-06, and _generated-index.json declares it expected_verify: true with expiry_check: "pass".
Line 69 of score-runner.mjs shows why nothing catches it: the reference runner checks expires_at > NOW and never reads issued_at. Only one side of the validity window is implemented, so the other side has no opinion to disagree with.
A runner that enforces both boundaries rejects those 32 cards and is scored wrong for it. Stage scoring is what makes that expensive. An expiry_check mismatch on its own now marks the vector FAILED even when the boolean agrees, so the machinery you added to punish over-rejection is currently punishing a stricter-than-reference validity check. The fixed suite can't surface this either: expired-atc closes the upper bound and there is no card that is merely early.
One line fixes the generator (derive issued_at from now, or clamp it). The reference runner needs the lower bound too, and a premature-atc vector would close the window from the other end.
2. Deleting one unsigned sidecar inverts the scoring.
Line 184 of the scorer reads expected_verify: meta ? meta.expected_verify : true. The expectations for generated cards exist only in _generated-index.json, written into the same directory as the challenge.
We generated 4 cards in --mode self-signed and scored the identical cards twice:
_generated-index.json
always-true
reference
present
0/4
4/4
deleted
4/4
0/4
That file was the only thing that changed.
Both directions hurt the challenge story. Hand someone ./challenge and you have handed them the answer key with it. Strip the answer key first and the true default awards a perfect score to a runner that accepts everything, which is a cheaper shortcut than the memorizer you built the generator to kill.
The expectation looks derivable without the sidecar: ca-test-2's public key is published, and a self-signed card is identifiable by its identity key sitting outside the pinned set. Short of that, dropping the true fallback and erroring out when a generated card's mode can't be established would at least fail closed.
Would you add premature-atc and turn the missing sidecar into a hard scoring error, then rerun the same seed and the self-signed set?
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
v1.3.0/v1.3.1 closes all three of the gaps we raised, and the stage scoring in particular does what it says. We ran the generator and the scorer against them. Two new problems came out, and one of them is caused by the stage scoring you just added.
1. The generator marks cards that are not yet valid as must-accept.
Line 97 of
generate-accept-vectors.mjsdraws the issue year withconst year = 2026 + Math.floor(rand() * 2), then randomizes month and day. So roughly half the output is dated ahead of the clock. With--count 60 --seed 7, 32 of 60 cards came out with anissued_atin the future.ATC-GEN-D4EEC409is dated2027-12-06, and_generated-index.jsondeclares itexpected_verify: truewithexpiry_check: "pass".Line 69 of
score-runner.mjsshows why nothing catches it: the reference runner checksexpires_at > NOWand never readsissued_at. Only one side of the validity window is implemented, so the other side has no opinion to disagree with.A runner that enforces both boundaries rejects those 32 cards and is scored wrong for it. Stage scoring is what makes that expensive. An
expiry_checkmismatch on its own now marks the vector FAILED even when the boolean agrees, so the machinery you added to punish over-rejection is currently punishing a stricter-than-reference validity check. The fixed suite can't surface this either:expired-atccloses the upper bound and there is no card that is merely early.One line fixes the generator (derive
issued_atfrom now, or clamp it). The reference runner needs the lower bound too, and apremature-atcvector would close the window from the other end.2. Deleting one unsigned sidecar inverts the scoring.
Line 184 of the scorer reads
expected_verify: meta ? meta.expected_verify : true. The expectations for generated cards exist only in_generated-index.json, written into the same directory as the challenge.We generated 4 cards in
--mode self-signedand scored the identical cards twice:_generated-index.jsonThat file was the only thing that changed.
Both directions hurt the challenge story. Hand someone
./challengeand you have handed them the answer key with it. Strip the answer key first and thetruedefault awards a perfect score to a runner that accepts everything, which is a cheaper shortcut than the memorizer you built the generator to kill.The expectation looks derivable without the sidecar:
ca-test-2's public key is published, and a self-signed card is identifiable by its identity key sitting outside the pinned set. Short of that, dropping thetruefallback and erroring out when a generated card's mode can't be established would at least fail closed.Would you add
premature-atcand turn the missing sidecar into a hard scoring error, then rerun the same seed and the self-signed set?