You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A repair can make a correction stick in exactly one way today: write manuallyLockedFields, which freezes the field against every future improvement (#2612). 107 of the 122 remaining lock instances exist for that reason alone. #2542 built the capability that was supposed to replace it, and #3153 gave a second source the ability to use it, but retraction answers a different question from the one these locks are asking.
Absence and refusal are different capabilities, and only absence exists. Field retraction handles "the page stopped saying it". These locks hold values that are wrong, not gone: a journal article page, a personal site, another institution's profile. The page still carries the value, so a correct source will keep asserting it and retraction will correctly never fire. The repo has now recorded this shape from three directions: a probe verdict alone is not a durable refusal (#2567), omission is not absence (#2647), and this.
Measured on Development
33 rows lock websiteUrl while storing no value. 28 live rival assertions block them. Running each through researchHomeWebsiteUrlDecision, the per-rule refusal vocabulary #3068 landed:
verdict
count
ADMITTED by every rule
22
yale-path-vocabulary
2
person-profile-or-directory-path
2
listing-or-index
2
Two separable findings.
The refusal vocabulary is audit-only. Its only caller in the whole tree is researchHomeWebsiteUrlRefusalAuditCore.ts. Nothing in the materialize or serve path consults it, so it names defects and prevents none. That is why 6 of the 28 blocking values are ones the repo can already classify as inadmissible and still derives anyway. Those 6 need no new capability, only a caller, and they are the cheap half of this issue.
The other 22 are genuinely per-row. No rule refuses them and none should: tedcohenlab.org is a real lab site, just not this row's. Admissibility here is a judgement about a value on a row, not about a URL shape, so no vocabulary can reach it.
What to build
A durable refusal record: a positive, dated, evidence-bearing statement that one specific stored value is not admissible at one field on one row, attributed to the rule or operation that refused it. Shape follows fieldLockProvenance, and the attribution vocabulary follows #3068.
Requirements, and the traps each one is designed against.
Readable by materialization. A repair persists by recording a refusal instead of writing a lock. That is the entire point of the issue.
It removes a candidate, it does not pin a field. A refusal keys on a normalized value, so a later, better value at the same field is untouched. This is the answer to "have you reinvented the lock": a lock makes the field unreachable, a refusal makes one value unreachable and lets the corpus decide among the rest.
Withdrawable. An explicit withdrawnAt plus reason, retired by a reviewed operation, so a refusal recorded under a rule that later changes does not become permanent.
Not expressible as superseded, and this is provable rather than asserted.superseded retires one observation row; a re-scrape mints a fresh live row carrying the same value, which is precisely the fix(scrapers): the observation engine cannot retract a field a source stops asserting, so a stale websiteUrl is permanent #2542 mechanism and why re-scraping does not retract a dead sourceUrl. A refusal is a standing judgement about a (row, field, value) triple, so it survives re-observation. The same argument rules out the four rollback reasons already in use: they are retirement records attached to observations, not admissibility rules about values. 181,047 rows carry superseded and not one of them can stop a value coming back.
Definition of done
Stored-data, so merging delivers nothing. Done is: a refusal recorded against one of the 22 on Development, materialize run, the wrong value shown not to return, that row's lock released, and the served surface re-read. A lock released without the refusal actually holding is the August 2026 regression in a new place. Mutation-checked both ways: a refusal that suppresses a good value must turn a test red, and a missing refusal must let the wrong value back.
Summary
A repair can make a correction stick in exactly one way today: write
manuallyLockedFields, which freezes the field against every future improvement (#2612). 107 of the 122 remaining lock instances exist for that reason alone. #2542 built the capability that was supposed to replace it, and #3153 gave a second source the ability to use it, but retraction answers a different question from the one these locks are asking.Absence and refusal are different capabilities, and only absence exists. Field retraction handles "the page stopped saying it". These locks hold values that are wrong, not gone: a journal article page, a personal site, another institution's profile. The page still carries the value, so a correct source will keep asserting it and retraction will correctly never fire. The repo has now recorded this shape from three directions: a probe verdict alone is not a durable refusal (#2567), omission is not absence (#2647), and this.
Measured on Development
33 rows lock
websiteUrlwhile storing no value. 28 live rival assertions block them. Running each throughresearchHomeWebsiteUrlDecision, the per-rule refusal vocabulary #3068 landed:yale-path-vocabularyperson-profile-or-directory-pathlisting-or-indexTwo separable findings.
The refusal vocabulary is audit-only. Its only caller in the whole tree is
researchHomeWebsiteUrlRefusalAuditCore.ts. Nothing in the materialize or serve path consults it, so it names defects and prevents none. That is why 6 of the 28 blocking values are ones the repo can already classify as inadmissible and still derives anyway. Those 6 need no new capability, only a caller, and they are the cheap half of this issue.The other 22 are genuinely per-row. No rule refuses them and none should:
tedcohenlab.orgis a real lab site, just not this row's. Admissibility here is a judgement about a value on a row, not about a URL shape, so no vocabulary can reach it.What to build
A durable refusal record: a positive, dated, evidence-bearing statement that one specific stored value is not admissible at one field on one row, attributed to the rule or operation that refused it. Shape follows
fieldLockProvenance, and the attribution vocabulary follows #3068.Requirements, and the traps each one is designed against.
withdrawnAtplus reason, retired by a reviewed operation, so a refusal recorded under a rule that later changes does not become permanent.superseded, and this is provable rather than asserted.supersededretires one observation row; a re-scrape mints a fresh live row carrying the same value, which is precisely the fix(scrapers): the observation engine cannot retract a field a source stops asserting, so a stale websiteUrl is permanent #2542 mechanism and why re-scraping does not retract a deadsourceUrl. A refusal is a standing judgement about a (row, field, value) triple, so it survives re-observation. The same argument rules out the four rollback reasons already in use: they are retirement records attached to observations, not admissibility rules about values. 181,047 rows carrysupersededand not one of them can stop a value coming back.Definition of done
Stored-data, so merging delivers nothing. Done is: a refusal recorded against one of the 22 on Development, materialize run, the wrong value shown not to return, that row's lock released, and the served surface re-read. A lock released without the refusal actually holding is the August 2026 regression in a new place. Mutation-checked both ways: a refusal that suppresses a good value must turn a test red, and a missing refusal must let the wrong value back.
Refs #2612, #2542, #3068, #3135.