Repository navigation
fix(dacs-directory): bounded revocation work, owner-scoped deals and separate retry classes - #94
Merged
randomblocker merged 4 commits intoOct 10, 2026
Conversation
… fair retries - Keep every revocation candidate per listing; 16 is now a per-pass verification budget (verified markers, then owner-anchored candidates, then arrival order). Unreadable candidates rotate; failed ones are re-examined only after a new write. - Key discovered deals by the bundle's buyer plus jobId in the scanner and persisted scan state (rekeyed in place on load, schema stays v9); /api/dacs/deal-owners searches across owners with the same response. - Count rejections and transient failures separately (rejection_count, deferred_at migrated in place); transient retries first, at most 20 rejected reads per pass, 1,000 scheduled rejected locators with rotation. - Check registration binding counts before verifying, verify bindings one at a time, bound producer-side resolver reads, and report duplicate owner/name programs as indeterminate (artifact route answers 409). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…jobId, separate retry classes Revocation candidates move into an indexed SQLite table, so each pass does a fixed amount of work per listing however long the queue is. Discovered deals are keyed by the buyer bundle's storage owner and the jobId in the scanner, scan state, catalog and deduplication, so two owners' deals with the same jobId stay separate. Rejected content and transient read failures are scheduled as separate retry classes, with a reserved share of retry reads and a ceiling on scheduled rejections. Bundle-binding discovery is bounded per signer, as DACS-5 BB-6 requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r parties An entry counts towards a deal's owners only when each bundle ref it carries is owned by, or bound through a verified BundleBinding to, the party it is reported for. A seller copy is attached to a discovered deal only when the deal has an attributed seller who owns that copy, and the lookup accepts an optional bundleRef to narrow it to one deal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… verified or scanned A catalog entry counts only when it sits in its own seller's record and either the indexer verified both bundle copies against its parties, or it matches a scanned entry as indexed. A scanned entry already in the catalog counts once, whichever bundle ref is requested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
randomblocker
marked this pull request as ready for review
October 10, 2026 13:10
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
randomblocker
added a commit
that referenced
this pull request
Oct 13, 2026
Brings #92-#94 (bounded verification work, owner-keyed deals, malformed artifact containment) under #85's SDK-aligned listing admission. Conflicts, resolved semantically: - store.ts: keep both startup migrations (#85 listing_rejections coordinates; #94 scan_runs omission counters, rejected-retry classes, revocation_candidates table). - chain.ts: keep #94's boundedJson guard on storage reads and #85's extended readback fields (storageAddress, metadata, tx refs). - indexer.ts: #85's admission (verifyListingResult with rejection diagnostics, validity window, `revocation`, transactionReadiness) over #94's bounded, owner-first revocation candidate batch and progress. - reindexCore.ts: #94's design (candidates live in SQLite, not scan state) with the public `revocation` field name. Semantic fixes the clean auto-merge did not cover: - verifyListingResult fails closed instead of throwing, keeping #94's "no canonical form" containment for every caller. - verifyListingRevocation: the pinned SDK's stripSignature now rejects non-canonical JSON, so it moves inside #94's existing guard. - scan.ts: SDK f68fed1 renamed the MVP agreement predicate; use isLegacyMvpAgreementDocument (byte-identical to 44d8ff2's isAgreementDocument that #94 was written against). - #94 tests: legacy listing fixtures use the SDK legacy-MVP string signature, current fixtures carry a DPA-1 verificationMethod, the revocation field is `revocation`, and the depth sweep follows the pinned SDK's 64-level canonical bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 13, 2026
randomblocker
added a commit
that referenced
this pull request
Oct 15, 2026
…10.5.1) DACS-5 §10.5.1 says the deriver "MUST collapse the input to one authoritative bundle per jobId before partitioning". Canonically equal copies collapse, a fully signed copy takes precedence over lesser-signed divergent ones, and equal standing voids the jobId (BB-6). #94 keyed the dedupe on (buyer, jobId), so two buyers' fully signed completed copies for one seller and jobId counted twice. A conforming deriver gives zero. - dedupeVerifiedDeals keeps one record per bundle ref and per (jobId, bundleContentHash). Among divergent copies of a jobId it keeps a lone fully signed one; any other verified copy stays listed with reputationEligible false. - DealRecord gains fullSignatureStanding, set from the BB-6 inspection on the current path and from the SDK signature verdicts on the legacy path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
randomblocker
added a commit
that referenced
this pull request
Oct 15, 2026
…otes - README: name the Standard, SDK and node revisions the verdicts depend on and the node's storage-read contract; list ARTIFACT_REJECTED and its retry class; describe the binding ceiling as it is since #94 (32 per signer of each job and role, no total across signers). - SPEC_PIN: the did:demos:agent profile is Ed25519, so listings signed with ecdsa-secp256k1 or sr1-aggregate are not verified. - The SDK repository is public; the token is only for a private mirror (README, setup-sdk.sh, CI). - The sessionAnchorName import comments: upstream exported it after the pinned SDK_REV. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Base: Community
main(4cdb14c), which includes the indexer containment work from #93. Onlyreference-implementations/dacs-directory/changes.This PR makes the directory's indexer do a fixed amount of work per listing on each pass. It keeps each owner's deal and binding records apart from records other owners write for the same
jobId, and the deal-owner lookup reports a deal only from refs bound to its parties, using catalog entries only when the indexer verified them or derived them from the scan. It also schedules rejected content and transient read failures as separate retry classes.What changes
Revocation candidates live in an indexed store
revocation_candidates), keyed by listing content hash and locator. Each row records the candidate's storage-program owner, the content hash it was read with, and its arrival order.REVOCATION_VERIFICATIONS_PER_PASS(16) candidates per listing, using two indexed queries of at most 16 rows each. The queue is never loaded or scanned as a whole.Deals are scoped by bundle owner and jobId everywhere
jobId. The scanner, persisted scan state, the synthetic registrations that build the catalog, and verified-deal deduplication all use this key, so two buyers' deals that share ajobIdstay separate records./api/dacs/deal-ownersnow does the following:jobId.owners: null. A lone unattributed entry is not reported as ownership.bundleRefparameter that narrows the lookup to the deal holding that bundle copy. The narrowing applies after the steps above. The deal verifier and the verify page pass the bundle they already hold.Bundle-binding discovery is bounded per signer
(jobId, role), as DACS-5 BB-6 requires. Overflow makes only that signer's side indeterminate.jobIdneither change how the deal is verified nor add verification work.Rejected content and transient failures are separate retry classes
artifactshas separate counters:rejection_countandretry_count.STORAGE_INVALID_RESPONSEis kept for malformed node responses.Smaller fixes
resolveOwnedAnchorByNamereads search and candidate responses through the same 1 MiB streaming ceiling and artifact preflight as the scanner and indexer.409with the existing{ error }body;Persistence and migration
revocation_candidatesin its stored order, with owners taken from the artifact table. This runs in one SQLite transaction and does nothing once the JSON queue is empty.artifactsgainsrejection_countanddeferred_atin place.rejection_count.Bounds
(jobId, role)jobId, plus an in-memory comparison of each such catalog entry with the scanned entries for thatjobIdAt the default 15-minute interval, a full set of 1,000 scheduled rejected locators is re-read about every 12.5 hours.
Wire formats, hashes, signature domains, public response shapes, package pins and vendored files are unchanged. Every valid artifact keeps its classification and hash.
Tests
test/scan-fairness.test.ts(28 tests), covering:bundleRefnarrowing;test/startup-migration.test.ts(3 tests): the startup ceiling, the order in which deferred rows return, and the move of the JSON candidate queue.test/retry-class-migration.test.ts(2 tests): retry-class migration, including more than 1,000 rejected rows.test/bundle-binding.test.tsandtest/current-indexer.test.tscover the per-signer binding ceiling, at the unit level and in the indexer.malformed-artifacts: the queue is read from the table, and content-policy failures are classified as rejections;scan-robustness: rejection observations are flagged;reindex-chain-reset: schema 10.scan-fairnessfail, each on its own assertiontsc --noEmitpassesNot in this PR
DACS_SCAN_MAX_TXS. A pass that cannot do so fails with the existing message and leaves the state unmigrated.jobIdalone returnsowners: nullwhenever more than one entry exists for thatjobId. PassingbundleRefnarrows it. Listing the candidates is a possible follow-up.jobId. The deal-owner lookup does not depend on that order. Revisiting the precedence is a follow-up.🤖 Generated with Claude Code