[{"data":1,"prerenderedAt":446},["ShallowReactive",2],{"navigation_docs":3,"-design-adrs-005-shared-denotation":101,"-design-adrs-005-shared-denotation-surround":442},[4,23,66],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":22},"Guide","i-lucide-book-open","\u002Fguide","1.guide",[10,14,18],{"title":11,"path":12,"stem":13},"Introduction","\u002Fguide\u002Fintroduction","1.guide\u002F1.introduction",{"title":15,"path":16,"stem":17},"Getting started","\u002Fguide\u002Fgetting-started","1.guide\u002F2.getting-started",{"title":19,"path":20,"stem":21},"Pieces and sets","\u002Fguide\u002Fpieces-and-sets","1.guide\u002F3.pieces-and-sets",false,{"title":24,"icon":25,"path":26,"stem":27,"children":28},"Design (ADRs)","i-lucide-drafting-compass","\u002Fdesign","2.design",[29,32],{"title":30,"path":26,"stem":31},"Design decisions (ADRs)","2.design\u002Findex",{"title":33,"icon":34,"path":35,"stem":36,"children":37,"page":22},"Decision records","i-lucide-scroll-text","\u002Fdesign\u002Fadrs","2.design\u002Fadrs",[38,42,46,50,54,58,62],{"title":39,"path":40,"stem":41},"ADR-001: The refinement-chain model for calendar points","\u002Fdesign\u002Fadrs\u002F001-refinement-chain","2.design\u002Fadrs\u002F001-refinement-chain",{"title":43,"path":44,"stem":45},"ADR-002: Sets first — the Span algebra","\u002Fdesign\u002Fadrs\u002F002-set-primary-spans","2.design\u002Fadrs\u002F002-set-primary-spans",{"title":47,"path":48,"stem":49},"ADR-003: Overflow policy — clamp by default, skip on request","\u002Fdesign\u002Fadrs\u002F003-overflow-policy","2.design\u002Fadrs\u002F003-overflow-policy",{"title":51,"path":52,"stem":53},"ADR-004: Fortnights as every-k scopes","\u002Fdesign\u002Fadrs\u002F004-fortnight-scopes","2.design\u002Fadrs\u002F004-fortnight-scopes",{"title":55,"path":56,"stem":57},"ADR-005: One denotation function, and what DST does to it","\u002Fdesign\u002Fadrs\u002F005-shared-denotation","2.design\u002Fadrs\u002F005-shared-denotation",{"title":59,"path":60,"stem":61},"ADR-006: Zocam is a standalone, released library","\u002Fdesign\u002Fadrs\u002F006-standalone-library","2.design\u002Fadrs\u002F006-standalone-library",{"title":63,"path":64,"stem":65},"ADR-007: A set is one value — struct answers, enumerable shapes","\u002Fdesign\u002Fadrs\u002F007-one-value-sets","2.design\u002Fadrs\u002F007-one-value-sets",{"title":67,"path":68,"stem":69,"children":70,"icon":100},"API Reference","\u002Fapi","3.api\u002Findex",[71,72,76,80,84,88,92,96],{"title":67,"path":68,"stem":69},{"title":73,"path":74,"stem":75},"Zocam","\u002Fapi\u002Fzocam","3.api\u002Fzocam",{"title":77,"path":78,"stem":79},"Zocam.Intervals","\u002Fapi\u002Fzocam-intervals","3.api\u002Fzocam-intervals",{"title":81,"path":82,"stem":83},"Zocam.ISO","\u002Fapi\u002Fzocam-iso","3.api\u002Fzocam-iso",{"title":85,"path":86,"stem":87},"Zocam.Point","\u002Fapi\u002Fzocam-point","3.api\u002Fzocam-point",{"title":89,"path":90,"stem":91},"Zocam.Point.ComposeError","\u002Fapi\u002Fzocam-point-composeerror","3.api\u002Fzocam-point-composeerror",{"title":93,"path":94,"stem":95},"Zocam.Span","\u002Fapi\u002Fzocam-span","3.api\u002Fzocam-span",{"title":97,"path":98,"stem":99},"Zocam.Span.Arc","\u002Fapi\u002Fzocam-span-arc","3.api\u002Fzocam-span-arc","i-lucide-braces",{"id":102,"title":55,"body":103,"description":131,"extension":436,"links":437,"meta":438,"navigation":439,"path":56,"seo":440,"stem":57,"__hash__":441},"docs\u002F2.design\u002Fadrs\u002F005-shared-denotation.md",{"type":104,"value":105,"toc":429},"minimark",[106,124,129,132,136,139,156,167,170,174,181,199,222,226,234,274,279,282,302,305,357,364,368,373,384,389,412,425],[107,108,109,115,116,123],"p",{},[110,111,114],"span",{"className":112},[113],"ai-slop","AI SLOP"," an AI agent wrote this page. ",[117,118,122],"a",{"href":119,"rel":120},"https:\u002F\u002Fgithub.com\u002Fyuri4n",[121],"nofollow","yuri4n",", a senior engineer, gave the direction and did the review. The review is human, thus errors can stay.",[125,126,128],"h2",{"id":127},"status","Status",[107,130,131],{},"Accepted, 2026-08-04, decided by the project owner.",[125,133,135],{"id":134},"context","Context",[107,137,138],{},"Two operations answer the same question: \"what does this point mean on the real timeline?\"",[140,141,142,150],"ul",{},[143,144,145,149],"li",{},[146,147,148],"code",{},"Span.member?\u002F2"," asks it for one instant. It is symbolic: no horizon, no enumeration.",[143,151,152,155],{},[146,153,154],{},"Span.ground\u002F3"," asks it for a whole horizon. It enumerates into the linear kernel.",[107,157,158,159,162,163,166],{},"If the two are two implementations, their answers can drift. This was a real defect class, caught in review: \"the 31st\" in February. Under ",[146,160,161],{},":clamp"," the point fires on Feb 28 (see ",[117,164,165],{"href":48},"ADR-003","). One implementation clamped; the other did not. The same instant got two different answers from one span.",[107,168,169],{},"The force behind the drift is duplication. Clamping, ordinal skipping, and closings are subtle rules. Two copies of a subtle rule always separate over time.",[125,171,173],{"id":172},"options","Options",[107,175,176,180],{},[177,178,179],"strong",{},"Option A: two independent implementations."," Each query optimizes its own path. Caveat: this is the drift that already happened. Nothing forces the copies to agree.",[107,182,183,194,195,198],{},[177,184,185,186,189,190,193],{},"Option B: define ",[146,187,188],{},"member?"," as a call to ",[146,191,192],{},"ground"," over a one-instant horizon."," Correct by construction. Caveat: it turns a symbolic question into an enumeration. ",[146,196,197],{},"nth"," grounding widens to whole cycle instances first, so even a one-instant probe can enumerate a full month. The cost model becomes wrong for the common query.",[107,200,201,204,205,208,209,212,213,217,218,221],{},[177,202,203],{},"Option C: a shared kernel."," One clamp-aware denotation function holds all the rules. ",[146,206,207],{},"member?\u002F2"," and ",[146,210,211],{},"ground\u002F3"," are thin interpreters over it. The pattern is a ",[214,215,216],"em",{},"single source of truth",": two query interfaces, one shared kernel. This extends the interpreter pattern from ",[117,219,220],{"href":44},"ADR-002",": the span tree is the free algebra, and both interpreters now share their semantics.",[125,223,225],{"id":224},"decision","Decision",[107,227,228,229,208,231,233],{},"Option C is the chosen one. There is one denotation function. ",[146,230,207],{},[146,232,211],{}," both call it. Clamping, ordinal skipping, and closings behave identically in both.",[235,236,241],"pre",{"className":237,"code":238,"language":239,"meta":240,"style":240},"language-mermaid shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","graph LR\n  P[\"Point\"] -- \"Span.of\u002F1\" --> S[\"Span\"]\n  S -- \"member?\u002F2\" --> D[\"one denotation\u003Cbr\u002F>function\"]\n  S -- \"ground\u002F3 (tz)\" --> D\n  D -- \"intervals on\u003Cbr\u002F>the timeline\" --> K[\"Zocam.Intervals\u003Cbr\u002F>(linear kernel)\"]\n","mermaid","",[146,242,243,250,256,262,268],{"__ignoreMap":240},[110,244,247],{"class":245,"line":246},"line",1,[110,248,249],{},"graph LR\n",[110,251,253],{"class":245,"line":252},2,[110,254,255],{},"  P[\"Point\"] -- \"Span.of\u002F1\" --> S[\"Span\"]\n",[110,257,259],{"class":245,"line":258},3,[110,260,261],{},"  S -- \"member?\u002F2\" --> D[\"one denotation\u003Cbr\u002F>function\"]\n",[110,263,265],{"class":245,"line":264},4,[110,266,267],{},"  S -- \"ground\u002F3 (tz)\" --> D\n",[110,269,271],{"class":245,"line":270},5,[110,272,273],{},"  D -- \"intervals on\u003Cbr\u002F>the timeline\" --> K[\"Zocam.Intervals\u003Cbr\u002F>(linear kernel)\"]\n",[107,275,276],{},[214,277,278],{},"Figure 1 — Both queries pass through one denotation function into the linear kernel. AI generated, human reviewed.",[107,280,281],{},"A property test pins the law, so the sharing cannot silently break:",[283,284,285],"blockquote",{},[107,286,287,290,291,294,295,298,299,301],{},[146,288,289],{},"member?(span, t)"," is true exactly when ",[146,292,293],{},"t"," falls inside some interval of ",[146,296,297],{},"ground(span, horizon, tz)",", for every ",[146,300,293],{}," in the horizon.",[107,303,304],{},"The precise DST rules:",[140,306,307,320,347],{},[143,308,309,315,316,319],{},[177,310,311,312,314],{},"Grounding happens only in ",[146,313,154],{},"."," ",[146,317,318],{},"Point"," stays timezone-free and pure. The calendar meets the real timeline in one place.",[143,321,322,325,326,329,330,333,334,337,338,315,341,315,344,314],{},[177,323,324],{},"Fall-back:"," the wall clock repeats an hour, so the preimage of a wall window is discontiguous. One instance grounds to one ",[214,327,328],{},"or more"," kernel intervals. The pieces are never joined: in ",[146,331,332],{},"America\u002FNew_York"," on 2026-11-01, wall ",[146,335,336],{},"00:30..01:30"," grounds to ",[146,339,340],{},"04:30Z..05:30Z",[214,342,343],{},"and",[146,345,346],{},"06:00Z..06:30Z",[143,348,349,352,353,356],{},[177,350,351],{},"Spring-forward:"," the wall time does not exist. The instance is skipped; it grounds to nothing. Wall ",[146,354,355],{},"02:30"," on 2026-03-08 yields an empty result.",[107,358,359,360,363],{},"The 2026 fixtures in ",[146,361,362],{},"test\u002Fzocam\u002Fspan_test.exs"," pin these real dates. All calendar facts in them are machine-verified.",[125,365,367],{"id":366},"consequences","Consequences",[107,369,370],{},[177,371,372],{},"Easy now:",[140,374,375,378,381],{},[143,376,377],{},"You add a new denotation feature (a policy, an ordinal rule, a closing) once. Both queries then agree by construction.",[143,379,380],{},"The \"31st in February\" defect class needs review at one site, not two.",[143,382,383],{},"The law is a cheap regression check: any probe instant can test it.",[107,385,386],{},[177,387,388],{},"Hard now:",[140,390,391,396],{},[143,392,393,395],{},[146,394,207],{}," cannot take a shortcut that the shared function does not offer. Every optimization must keep the law.",[143,397,398,399,401,402,405,406,408,409,411],{},"The two queries answer at different levels: ",[146,400,207],{}," reads the wall clock of the given ",[146,403,404],{},"DateTime",", and ",[146,407,211],{}," maps wall time to the timeline. The law still holds through DST. A gap instant has no ",[146,410,404],{}," in that zone, and both fall-back instants map to wall times inside the window.",[107,413,414,417,418,421,422,424],{},[177,415,416],{},"Open items:"," the law holds in the current test suite; a generated property test is still a good next step. ",[146,419,420],{},"stream\u002F3"," keeps using the same denotation today, because it grounds chunk by chunk with ",[146,423,211],{},". A later change must keep that path.",[426,427,428],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":240,"searchDepth":252,"depth":252,"links":430},[431,432,433,434,435],{"id":127,"depth":252,"text":128},{"id":134,"depth":252,"text":135},{"id":172,"depth":252,"text":173},{"id":224,"depth":252,"text":225},{"id":366,"depth":252,"text":367},"md",null,{},true,{"title":55,"description":131},"YPRZ0buDWc3muXNGaXUTNJuGwqHt8bs_lI7UE_scZ5w",[443,444],{"title":51,"path":52,"stem":53,"description":131,"children":-1},{"title":59,"path":60,"stem":61,"description":445,"children":-1},"Accepted, 2026-08-04, decided by the project owner (\"separate it as a library\"). Revised 2026-08-05: the chosen shape is now a separate repository that publishes to Hex.",1786065169458]