[{"data":1,"prerenderedAt":437},["ShallowReactive",2],{"navigation_docs":3,"-design-adrs-004-fortnight-scopes":101,"-design-adrs-004-fortnight-scopes-surround":434},[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":51,"body":103,"description":131,"extension":428,"links":429,"meta":430,"navigation":431,"path":52,"seo":432,"stem":53,"__hash__":433},"docs\u002F2.design\u002Fadrs\u002F004-fortnight-scopes.md",{"type":104,"value":105,"toc":421},"minimark",[106,124,129,132,136,154,157,160,164,208,212,222,262,267,270,311,325,329,334,351,356,376,382,417],[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,139,143,144,143,147,143,150,153],{},"Real schedules use rhythms that are coarser than one cycle. \"Every second week\" is a fortnight. \"Every third month\" is a quarter rota. The base cycles (",[140,141,142],"code",{},":year",", ",[140,145,146],{},":month",[140,148,149],{},":week",[140,151,152],{},":day",") repeat on every instance, so they cannot express these rhythms.",[107,155,156],{},"A rhythm also needs a phase. Two teams can both meet \"every other Wednesday\" and never meet in the same week. The model must record which weeks a point keeps.",[107,158,159],{},"Payroll and rota cases need fortnights immediately. This feature is part of v1, not an extension.",[125,161,163],{"id":162},"options","Options",[165,166,167,179,198],"ol",{},[168,169,170,178],"li",{},[171,172,173,174,177],"strong",{},"A dedicated ",[140,175,176],{},":fortnight"," cycle unit."," Caveat: it does not generalize. \"Every third week\" and \"every second month\" would each need a new unit. Each unit would also need its own calendar arithmetic. This is an ad-hoc design.",[168,180,181,184,185,189,190,193,194,197],{},[171,182,183],{},"A step inside arcs."," An arc step samples cells ",[186,187,188],"em",{},"inside one instance"," (see ",[117,191,192],{"href":44},"ADR-002","). Caveat: a step cannot thin the ",[186,195,196],{},"stream of instances"," itself. The rhythm lives one level higher than the step, so this is the wrong layer.",[168,199,200,207],{},[171,201,202,203,206],{},"A scope wrapper ",[140,204,205],{},"{:every, k, cycle, anchor}","."," This is the decorator pattern: the wrapper changes the rhythm of a base cycle and keeps everything else. Caveat: the anchor adds one identity-like field, and the API must protect it.",[125,209,211],{"id":210},"decision","Decision",[107,213,214,215,218,219,221],{},"Option 3 is the chosen one, with ",[171,216,217],{},"subsequence semantics",". The scope ",[140,220,205],{}," keeps every k-th instance of the base cycle, counted from the instance that contains the anchor date. Each kept instance keeps the size of the base cycle: a fortnight point still denotes week-sized things, not 14-day things.",[223,224,229],"pre",{"className":225,"code":226,"language":227,"meta":228,"style":228},"language-mermaid shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","flowchart LR\n    W1[\"week of Jan 5\u003Cbr\u002F>anchor Jan 7 — kept\"] --> W2[\"week of Jan 12\u003Cbr\u002F>dropped\"]\n    W2 --> W3[\"week of Jan 19\u003Cbr\u002F>kept\"] --> W4[\"week of Jan 26\u003Cbr\u002F>dropped\"]\n    style W1 stroke-width:3px\n    style W3 stroke-width:3px\n","mermaid","",[140,230,231,238,244,250,256],{"__ignoreMap":228},[110,232,235],{"class":233,"line":234},"line",1,[110,236,237],{},"flowchart LR\n",[110,239,241],{"class":233,"line":240},2,[110,242,243],{},"    W1[\"week of Jan 5\u003Cbr\u002F>anchor Jan 7 — kept\"] --> W2[\"week of Jan 12\u003Cbr\u002F>dropped\"]\n",[110,245,247],{"class":233,"line":246},3,[110,248,249],{},"    W2 --> W3[\"week of Jan 19\u003Cbr\u002F>kept\"] --> W4[\"week of Jan 26\u003Cbr\u002F>dropped\"]\n",[110,251,253],{"class":233,"line":252},4,[110,254,255],{},"    style W1 stroke-width:3px\n",[110,257,259],{"class":233,"line":258},5,[110,260,261],{},"    style W3 stroke-width:3px\n",[107,263,264],{},[186,265,266],{},"Figure 1 — Subsequence semantics: a fortnight keeps every second week, counted from the anchor. AI generated, human reviewed.",[107,268,269],{},"The precise rules:",[271,272,273,279,286,293],"ul",{},[168,274,275,278],{},[140,276,277],{},"Point.every\u002F3"," builds these scopes. It accepts only points with a plain cycle scope.",[168,280,281,282,285],{},"An ",[140,283,284],{},":absolute"," point is rejected: it happens once, so it cannot repeat.",[168,287,288,289,292],{},"Re-anchoring an existing ",[140,290,291],{},"{:every, ...}"," point is rejected. An anchor is identity, not a parameter to mutate. Nested anchors would hide which phase wins, so you restate the rhythm from the base cycle.",[168,294,295,298,299,302,303,306,307,310],{},[140,296,297],{},"compose\u002F2"," keeps an every-scoped ",[171,300,301],{},"outer"," point: the phase lives in the scope, and compose keeps the outer scope. An every-scoped ",[171,304,305],{},"inner"," point fails with reason ",[140,308,309],{},":anchored",", because a chain has no place for the phase.",[312,313,314],"blockquote",{},[107,315,316,317,320,321,324],{},"The class of ",[140,318,319],{},"{:every, k, cycle, _}"," is the class of its base cycle. ",[140,322,323],{},"k"," changes the rhythm of the instances, not their size. This one rule keeps all composition arithmetic unchanged.",[125,326,328],{"id":327},"consequences","Consequences",[107,330,331],{},[171,332,333],{},"Easy:",[271,335,336,339,345],{},[168,337,338],{},"Any rhythm over any cycle works: every 2nd week, every 3rd month, every 10th day. One wrapper covers them all.",[168,340,341,342,344],{},"No new calendar arithmetic. The class mapping folds the wrapper away, so ",[140,343,297],{}," and the validators do not change.",[168,346,347,348,206],{},"\"Every other Wednesday at 15:00\" composes as normal points, then takes the rhythm once with ",[140,349,350],{},"every\u002F3",[107,352,353],{},[171,354,355],{},"Hard:",[271,357,358,373],{},[168,359,360,361,364,365,368,369,372],{},"The phase check is not in ",[140,362,363],{},"Point",". ",[140,366,367],{},"Zocam.Span.of\u002F1"," and ",[140,370,371],{},"ground\u002F3"," must count instances from the anchor. A test pins this: the fortnightly Wednesday alternates from Jan 7, 2026.",[168,374,375],{},"Users must restate a rhythm to change it. This is a small cost, chosen on purpose.",[107,377,378,381],{},[171,379,380],{},"Open items:"," none. The rules are implemented:",[271,383,384,408],{},[168,385,386,387,390,391,394,395,398,399,401,402,368,405,407],{},"The phase arithmetic lives in the ",[140,388,389],{},"Span"," interpreters. ",[140,392,393],{},"base_cycle\u002F1"," folds the wrapper away to its base cycle, and ",[140,396,397],{},"selected_instance?\u002F3"," keeps an instance when its index is a whole multiple of ",[140,400,323],{}," away from the instance that holds the anchor. ",[140,403,404],{},"member?\u002F2",[140,406,371],{}," both pass through that filter, so they agree.",[168,409,410,413,414,416],{},[140,411,412],{},"stream\u002F3"," needs no rule of its own. It grounds chunk by chunk with ",[140,415,371],{},", so the same filter selects the kept instances after any start instant.",[418,419,420],"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":228,"searchDepth":240,"depth":240,"links":422},[423,424,425,426,427],{"id":127,"depth":240,"text":128},{"id":134,"depth":240,"text":135},{"id":162,"depth":240,"text":163},{"id":210,"depth":240,"text":211},{"id":327,"depth":240,"text":328},"md",null,{},true,{"title":51,"description":131},"6gw616EiuVMPHjp_-7F7aNi8tdN2_MDvbaZUd1fkqKk",[435,436],{"title":47,"path":48,"stem":49,"description":131,"children":-1},{"title":55,"path":56,"stem":57,"description":131,"children":-1},1786065169428]