feat: dual-z writer — local z on new tags/taggings (tag-federation Half 2 Part B)#311
Merged
Merged
Conversation
…ory 3, Part B) Stacked on feat/b-tag-primitive (PR #310). Story 3 = the dual-z writer: new tags/taggings carry both canonical z (network visibility) and local z (local concept-list membership). W11 stamping practice designed in the Architecture phase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…deration 0003) W11 floor applied: every new tag element + tagging assertion carries two z tags — canonical (82b75e47 literal, unchanged, Half-1 federates on) + local (runtime taPubkey, new, lands it in the instance's local concept list). Added inline at the two client writers; taPubkey threaded as an explicit arg (pure functions). Additive to the ADR-0022 hybrid e+a; no migration; client-only; non-throwing guard on missing local TA. Resolves OQ-1..4. No firmware change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 source-contract tests over the two ESM writers (7 red / 7 stay-green
baseline). Asserts each writer emits canonical z (unchanged const) + a NEW
runtime-interpolated local z (39998:${taPubkey}:<slug>), exactly two z
entries (dev-box-degenerate-proof: assert two emitted, not that they differ),
the ADR-0022 e+a shape intact (regression guard), no new hardcoded literal
for the local handle, the localTaPubkey param + threading from both call
sites, and a non-throw tripwire. AC-4/AC-5 live-only (Playwright recipe).
Registered in test/test.js (4 points).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…aggings (tag-federation story 3)
Implements ADR 0003. Each new tag element (createTag) + tagging assertion
(publishProfileTagAssertion) now stamps a SECOND z: the local
39998:${runtime taPubkey}:<slug>, alongside the unchanged canonical
39998:82b75e47…:<slug> (ADR-0015 literal). Lands locally-authored activity
in the instance's own concept list while staying network-visible.
- publishProfileTag.js: +localTaPubkey param; local z via conditional spread;
non-fatal warn+omit on missing/malformed local TA (never throws — a missing
local z has no network cost, canonical still ships).
- useProfileTags.js: read taPubkey via useConfig; thread localTaPubkey into the
assertion writer; same local z + guard in createTag.
- Tag.jsx: read taPubkey; thread localTaPubkey into handleApply/handleDispute.
- Additive to the ADR-0022 hybrid e+a (regression-guarded; that suite stays 10/0).
ALSO tightened the SOFT non-throw test in dual-z-writer.test.js: its 220-char-
window heuristic false-positived once localTaPubkey entered the signature (right
before the pre-existing window.nostr/authorPubkey throws). Refined to the precise
forbidden pattern — an if(…localTaPubkey…){…throw} guard. (Flagged for review.)
Tests: dual-z-writer 14/0; UI builds clean; no regression. Live AC-4/AC-5
(local list populates) = Playwright/manual recipe per the test plan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Canonical + local z on new tags/taggings (W11). Gates: dual-z 14/0, hybrid e+a regression 10/0, profile-tag-polish 11/0, UI build clean. Anti-hardcode confirmed (local z runtime, one canonical literal/file). SOFT non-throw test precision fix judged legitimate (removes a signature-induced false-positive; still catches a genuine throw-guard). AC-4/AC-5 deferred to deployed-env; AC-7 breadcrumb for the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Collaborator
|
Claude 4.8 did a 20 minute review on PR 311 and 310 and recommends OK to merge both. Good enough for me. |
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.
TL;DR
The dual-z writer — Part B of Half 2. Makes every new tag element and tagging assertion stamp a second
ztag (the local39998:<runtime instance TA>:<slug>) alongside the existing canonical39998:82b75e47…:<slug>. The canonical z keeps the event network-visible (Half-1 federates on it); the new local z lands it in this instance's own concept list so locally-authored activity actually shows up locally — instead of the local list staying empty while the tags are only visible network-wide.What this PR does
Implements
tag-federationStory 3 / ADR 0003 — the W11 stamping floor: a published tag/tagging carries its canonical (required) z plus a local z;zorder is not load-bearing (readers#z-union per value). Scoped to the tag stack's two coordinates — no cloud/cap/rotation (those stay resolver-gated incommunity-referenceADR 0033).Changelog
ui/src/utils/publishProfileTag.js(tagging-assertion writer) —+localTaPubkeyparam; appends a local z39998:${localTaPubkey}:nostr-user-tagafter the unchanged canonicalNOSTR_USER_TAG_HANDLE. Additive — the ADR-0022 hybride+ashape (['a', tagAddress],['e', tag.eventId],tagAddress = 39999:${tag.authorPubkey}:${tag.slug}) is untouched.ui/src/hooks/useProfileTags.js— reads the runtime TA viauseConfig().taPubkey; threadslocalTaPubkeyinto the assertion writer; stamps the same local z increateTag(the tag-element writer).ui/src/pages/Tag.jsx— readstaPubkey; threadslocalTaPubkeyinto bothhandleApplyandhandleDispute.Guarantees
taPubkey— no new pubkey literal introduced (each writer still has exactly one82b75e47…, the pre-existing const).Testing
test/dual-z-writer.test.js— 14/0 (source-contract over the two ESM writers + the call sites; registered intest/test.js).profile-tag-polish11/0.Live verification deferred to a deployed env
AC-4 (a new tagging lands in the instance's local list) + the distinct-z behavior are not verified here: on the dev box the local TA equals the canonical coordinate (
82b75e47…), so the two z's resolve to identical strings — the source-contract assertion of record is "two z entries emitted, the second runtime-sourced," not "two distinct values." The distinct-value case (and local-list population) manifests only on a non-dev instance, so it's left for whoever merges to a deployed env to confirm.This PR makes each new Tag and Tagging carry TWO
ztags: canonical (82b75e47…, network identity, unchanged) + local (the runtime instance TA, NEW, for the local concept list). Together with #310's onepointer-bper header (local → canonical), that's the full dual-z model: two z's per event, onebper concept header.This is where your "the new Tags and Taggings need to have two z-tags in them, not just one" lands — confirmed. (Open question still flagged in #310: you mentioned "two b-tags" on the concepts; we ship one pointer-
bper header per the wire spec — see #310's addendum for the reversal breadcrumb if you want a different shape.)Reversal breadcrumb: removing the second
['z', …]line from each writer (publishProfileTag.js,useProfileTags.js createTag) reverts to single-z behavior with no data migration.🤖 Generated with Claude Code