<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Webb</title>
    <description>The latest articles on DEV Community by David Webb (@davidwebb915).</description>
    <link>https://dev.to/davidwebb915</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4126319%2F0b58fdc5-2a36-41a6-92ae-ef1eaa23f487.png</url>
      <title>DEV Community: David Webb</title>
      <link>https://dev.to/davidwebb915</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davidwebb915"/>
    <language>en</language>
    <item>
      <title>How a 60-Cycle Calendar System Works as a 60-Year Periodic Algorithm</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Thu, 01 Oct 2026 06:45:53 +0000</pubDate>
      <link>https://dev.to/davidwebb915/how-a-60-cycle-calendar-system-works-as-a-60-year-periodic-algorithm-1c7b</link>
      <guid>https://dev.to/davidwebb915/how-a-60-cycle-calendar-system-works-as-a-60-year-periodic-algorithm-1c7b</guid>
      <description>&lt;p&gt;I started looking at BaZi, or Four Pillars of Destiny, because its output seemed oddly structured. It did not look like a paragraph of free-form symbolism. It looked like a compact record: four paired labels, each drawn from repeating sequences, with rules for translating a birth time into those labels.&lt;/p&gt;

&lt;p&gt;As a data engineer, I’m inclined to ask what the schema is, where the boundaries sit, and what happens when the input is ambiguous. Those questions don’t prove anything about the system’s claims. They do make its calendar mechanics interesting to reverse-engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A cycle built from two counters
&lt;/h2&gt;

&lt;p&gt;The basic unit is a pair: one Heavenly Stem and one Earthly Branch. There are 10 stems and 12 branches. Both advance one position at a time, so the first pair returns when both counters have completed whole cycles. That takes the least common multiple of 10 and 12: 60 steps.&lt;/p&gt;

&lt;p&gt;There’s a small combinatorics wrinkle. Ten times twelve gives 120 possible pairings if every stem could match every branch. But the traditional sequence advances both lists together, so only 60 pairings occur. Starting from Jia-Zi, the sequence proceeds Jia-Zi, Yi-Chou, Bing-Yin, and so on, until it returns to Jia-Zi.&lt;/p&gt;

&lt;p&gt;In code, I might represent a position with an integer &lt;code&gt;n&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stem index: &lt;code&gt;n mod 10&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Branch index: &lt;code&gt;n mod 12&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Full pair: the two indexed symbols&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advance &lt;code&gt;n&lt;/code&gt; by one to move to the next pair. Advance it by 60 and the pair is back where it started. That’s the core periodic algorithm: two clocks with different periods, sharing one step counter. (&lt;a href=""&gt;aa.usno.navy.mil&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The 60-step sequence can label years, months, days, and hours, but those are not one giant clock ticking at the same speed. Each unit has its own rule for finding the current position in the cycle. The shared encoding makes the labels comparable; the calendar conventions decide where one unit ends and the next begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning a timestamp into four pillars
&lt;/h2&gt;

&lt;p&gt;A chart has four pillars: year, month, day, and hour. Each pillar is one stem-branch pair. A simplified software pipeline looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse the birth timestamp, location, and time-zone context.&lt;/li&gt;
&lt;li&gt;Convert the timestamp to the calendar’s working local time.&lt;/li&gt;
&lt;li&gt;Determine which year and solar-term month contain it.&lt;/li&gt;
&lt;li&gt;Find the sexagenary day index.&lt;/li&gt;
&lt;li&gt;Divide the day into two-hour branch intervals, then derive the hour stem from the day stem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The year pillar is not always switched at the same boundary across every convention. Many BaZi methods use the solar term Li Chun as the year boundary rather than Lunar New Year. Month pillars also follow solar terms: the year is divided into 12 solar months, with month changes anchored to particular terms. So a person born in early February may land on different year labels under different year-boundary conventions.&lt;/p&gt;

&lt;p&gt;The day pillar is a position in its own 60-day sequence. The hour branch divides the day into 12 two-hour periods; for example, Zi is commonly associated with roughly 11 p.m. to 1 a.m. The hour stem is not an independent counter that can be looked up from clock time alone: its assignment is linked to the day stem.&lt;/p&gt;

&lt;p&gt;This is why two implementations can accept the same date and still disagree. They may be using different boundaries, different day-rollover conventions, or different assumptions about what “local time” means. The symbolic lookup can be perfectly consistent while the input normalization differs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Civil time is not solar time
&lt;/h2&gt;

&lt;p&gt;A timestamp such as &lt;code&gt;01:30&lt;/code&gt; is a statement about a clock, not directly about the Sun’s position. Civil clocks follow time-zone rules. Apparent solar time follows the Sun’s local position, which varies with longitude and with the equation of time.&lt;/p&gt;

&lt;p&gt;The IANA Time Zone Database is useful here because it records time-zone offsets and daylight-saving transitions for named regions. But historical coverage and confidence vary, especially for dates before 1970. A location name alone is not enough; a robust input needs a date and a specific zone, and an ambiguous local time may also need its UTC offset or an explicit “first or second occurrence” marker. (&lt;a href=""&gt;iana.org&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Consider New York on November 7, 2021. At the end of daylight saving time, clocks moved from 2:00 a.m. daylight time back to 1:00 a.m. standard time. That means &lt;code&gt;01:30&lt;/code&gt; occurred twice: once at UTC−4 and again at UTC−5. A timestamp containing only the city and wall-clock reading cannot distinguish those two instants. The IANA zone rules can resolve the transition once the input identifies which occurrence it means. (&lt;a href=""&gt;timeanddate.com&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If a method uses true solar time, there is another adjustment. First resolve the civil time-zone offset, including daylight saving time. Then account for longitude relative to the time zone’s standard meridian, and for the equation of time. As a rough rule, each degree of longitude corresponds to about four minutes of solar-time difference. New York is near 74° west, while Eastern Standard Time is based on a 75° west meridian, so the longitude correction is about four minutes ahead of standard clock time. The equation-of-time adjustment varies by date and can reach around 16 minutes. (&lt;a href=""&gt;aa.usno.navy.mil&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Those minutes matter most near a boundary. If the corrected time falls close to a two-hour branch change, different normalization choices could produce a different hour branch. Near a day boundary, a convention for when the sexagenary day turns over could affect the day pillar too. This is less a mystical mystery than a familiar data-quality issue: the model has edge cases, and the pipeline needs to state its assumptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ten-year phase as a state machine
&lt;/h2&gt;

&lt;p&gt;BaZi also describes longer luck cycles, often called decade pillars. In many methods, a sequence of these ten-year phases is derived from the month pillar. The direction of travel and the starting age depend on conventions that can involve the birth year and the distance to a nearby solar term. Each phase advances through the stem-branch sequence, with a new phase roughly every ten years.&lt;/p&gt;

&lt;p&gt;That invites a state-machine analogy. The birth chart is the initial state; the decade pillar is a phase that changes at a calculated boundary; the current year and other calendar cycles provide additional inputs. A reader can describe the state at a given date by identifying which phase is active and which cycle labels apply.&lt;/p&gt;

&lt;p&gt;The analogy has limits. A state machine tells us how labels change under stated rules. It does not establish that those labels cause or reliably describe events. Also, a ten-year phase is not simply “one sixth of a 60-year loop” in every practical calculation: its start and direction depend on conventions, and other calendar boundaries run alongside it.&lt;/p&gt;

&lt;p&gt;Still, the algorithmic structure is elegant. A small number of cyclic counters, plus carefully defined transition rules, produce a compact time-based representation. The trickiest part is not the modulo arithmetic. It is agreeing on the exact instant and the calendar rules before the arithmetic begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boundaries
&lt;/h2&gt;

&lt;p&gt;I treat BaZi as a cultural-computational system for insight and reflection, not a tool for forecasting outcomes. The cycle math can be described and tested; that does not validate claims that chart labels determine a person’s character or life events. I make no outcome claims here. At most, the system can serve as entertainment or a prompt for decision-support conversations, with real-world decisions grounded in evidence relevant to the decision itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm still unsure about
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Which time-normalization convention is most faithful to historical BaZi practice: civil clock time, local mean solar time, or apparent solar time?&lt;/li&gt;
&lt;li&gt;How much do different schools’ boundary rules change charts in practice, especially for births near midnight or a solar-term transition?&lt;/li&gt;
&lt;li&gt;Can a software implementation clearly expose uncertainty in old time-zone records without making the chart interface unreadable?&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>history</category>
      <category>algorithms</category>
      <category>calendar</category>
    </item>
    <item>
      <title>Birth times are messy inputs: timezones, DST, and solar time</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:45:50 +0000</pubDate>
      <link>https://dev.to/davidwebb915/birth-times-are-messy-inputs-timezones-dst-and-solar-time-35mp</link>
      <guid>https://dev.to/davidwebb915/birth-times-are-messy-inputs-timezones-dst-and-solar-time-35mp</guid>
      <description>&lt;p&gt;Title: Birth times are messy inputs: timezones, DST, and solar time&lt;/p&gt;

&lt;p&gt;I thought the evaluation engine would be the hard part. I was wrong; timestamp normalization took longer.&lt;/p&gt;

&lt;p&gt;A local civil-time reading has to account for historical daylight-saving changes, sub-hour offset adjustments, and longitude corrections before it can yield true solar time. Then come the edge cases: solar-term transitions, and the competing rules that flip a calendar day at 23:00 or midnight. Either choice can change the state initializer.&lt;/p&gt;

&lt;p&gt;Now I treat each raw birth timestamp as unvalidated input and send it through a strict timezone transformation pipeline.&lt;/p&gt;

&lt;p&gt;When building a software pipeline for this calendar system, the input stage is deceptively complex. A timestamp such as &lt;code&gt;01:30&lt;/code&gt; is a statement about a civil clock, not the Sun's position. Civil clocks follow regional time-zone rules, whereas apparent solar time depends on the Sun's actual position, shifting with longitude and the equation of time.&lt;/p&gt;

&lt;p&gt;I rely on the IANA Time Zone Database (zoneinfo) to handle historical offsets and daylight-saving transitions (DST). But historical coverage varies in accuracy, especially for dates prior to 1970. A location name alone is insufficient. To resolve an ambiguous timestamp, you need a date, a specific zone, and an explicit UTC offset or occurrence marker.&lt;/p&gt;

&lt;p&gt;Take New York on November 7, 2021. At the end of daylight saving time, clocks fell back from 2:00 a.m. daylight time to 1:00 a.m. standard time. That means &lt;code&gt;01:30&lt;/code&gt; occurred twice: once at UTC−4 and again at UTC−5. Without an explicit occurrence marker, a wall-clock reading cannot distinguish those two instants.&lt;/p&gt;

&lt;p&gt;If a calendar method requires true solar time, additional adjustments must be chained together. First, you resolve the civil time-zone offset and DST. Next, you apply a longitude correction relative to the zone's standard meridian. Each degree of longitude shifts solar time by roughly four minutes. New York sits near 74° west, while Eastern Standard Time is based on a 75° west meridian, placing New York about four minutes ahead of standard clock time. Finally, the equation of time must be applied, which fluctuates by date and can shift the result by up to 16 minutes.&lt;/p&gt;

&lt;p&gt;These minutes matter most near boundaries. The hour branch divides the day into 12 two-hour intervals, such as Zi from roughly 11 p.m. to 1 a.m. Because Zi begins at 11 p.m. (23:00), some conventions flip the calendar day at 23:00, while others wait until midnight. Because the hour stem is derived from the day stem, choosing 23:00 versus midnight flips both the day pillar and the hour stem. This is why two implementations receiving the same input date can yield different charts.&lt;/p&gt;

&lt;p&gt;I am still unsure about which time-normalization convention is most faithful to historical practice: civil clock time, local mean solar time, or apparent solar time. I also wonder how to clearly expose uncertainty in old zoneinfo records without making the chart interface unreadable—a common challenge when surfacing historical data.&lt;/p&gt;

&lt;p&gt;Not medical, legal, or financial advice. A cultural and cognitive framework for self-reflection; outputs vary by individual.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>algorithms</category>
      <category>history</category>
      <category>culture</category>
    </item>
    <item>
      <title>I tried to put a 2,000-year-old calendar into one boundary rule</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:45:13 +0000</pubDate>
      <link>https://dev.to/davidwebb915/i-tried-to-put-a-2000-year-old-calendar-into-one-boundary-rule-16im</link>
      <guid>https://dev.to/davidwebb915/i-tried-to-put-a-2000-year-old-calendar-into-one-boundary-rule-16im</guid>
      <description>&lt;p&gt;Title: I tried to put a 2,000-year-old calendar into one boundary rule&lt;/p&gt;

&lt;p&gt;I once tried to fit different traditional calendar schools into one boolean truth table. The disagreements looked like logic bugs until I saw the structural assumptions underneath.&lt;/p&gt;

&lt;p&gt;An ancient astronomical cycle's boundary depends on which solar coordinate or epoch anchor you choose as the primary axis. Each school has a self-consistent coordinate system and its own edge definitions; one truth table can't reconcile them without flattening those differences.&lt;/p&gt;

&lt;p&gt;Now my schema keeps core graph processing separate from boundary parsing. I made the parsing strategy configurable by calendar convention instead of baking one convention in as the absolute truth.&lt;/p&gt;

&lt;p&gt;Early on, I expected that translating a birth time into four pillars (year, month, day, and hour) would follow a single standard. When comparing outputs across different BaZi tools, I noticed they frequently disagreed on edge cases. For instance, the year pillar boundary is not uniformly set at Lunar New Year across all schools. Many conventions anchor the year transition to the solar term Lichun instead. A person born in early February can land on completely different year labels depending on which year-boundary convention the software enforces.&lt;/p&gt;

&lt;p&gt;Month pillars present similar branch points. The calendar divides the year into 12 solar months, anchoring month transitions to specific solar terms rather than lunar months. Meanwhile, the day pillar relies on its own 60-day position, with competing rules turning the day over at 23:00 or at midnight. Because the hour stem assignment is linked to the day stem, changing the day rollover boundary cascade-updates the hour pillar too.&lt;/p&gt;

&lt;p&gt;Longer ten-year phases, or decade pillars, add another layer of configuration. Derived from the month pillar, their direction and starting age depend on conventions involving the birth year and the distance to a nearby solar term. Each phase advances through the stem-branch sequence every ten years. This works like a state machine where the birth chart provides the initial state and the decade pillar updates at calculated transition points, though the machine merely models label shifts under stated rules.&lt;/p&gt;

&lt;p&gt;Attempting to hardcode all these rules into one rigid conditional block caused constant refactoring. The underlying modulo lookup is perfectly consistent; the divergence happens entirely during boundary parsing and input normalization.&lt;/p&gt;

&lt;p&gt;To clean up the system, I refactored the pipeline. Step one parses the timestamp, location, and timezone context. Step two converts the timestamp to working local time. Step three identifies the year and solar-term month. Step four calculates the sexagenary day index. Step five divides the day into two-hour intervals and derives the hour stem.&lt;/p&gt;

&lt;p&gt;I separated core cycle calculations from the boundary parsing rules. By keeping the schema modular and making boundary strategies configurable by calendar convention, the engine avoids declaring one school's edge definitions as universal truth.&lt;/p&gt;

&lt;p&gt;I am still unsure how much different schools' boundary choices alter charts across large population datasets, especially for births occurring near midnight or solar-term transitions. Accepting that distinct coordinate systems exist made the code far more maintainable.&lt;/p&gt;

&lt;p&gt;Not medical, legal, or financial advice. A cultural and cognitive framework for self-reflection; outputs vary by individual.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>algorithms</category>
      <category>history</category>
      <category>culture</category>
    </item>
    <item>
      <title>I found a 60-step cycle in a 2,000-year-old calendar</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:45:03 +0000</pubDate>
      <link>https://dev.to/davidwebb915/i-found-a-60-step-cycle-in-a-2000-year-old-calendar-aoc</link>
      <guid>https://dev.to/davidwebb915/i-found-a-60-step-cycle-in-a-2000-year-old-calendar-aoc</guid>
      <description>&lt;p&gt;Title: I found a 60-step cycle in a 2,000-year-old calendar&lt;/p&gt;

&lt;p&gt;While parsing a sexagenary calendar record, I kept getting hung up on two counters: one wraps at 10, the other at 12, and both advance together. Their least common multiple is 60, so the pair repeats as a 60-step state machine. No fractional adjustments needed.&lt;/p&gt;

&lt;p&gt;That fixed sequence indexes cyclical time markers across days, months, and years. I wrote it as a discrete modulo generator using integer arithmetic alone. It shows how ancient astronomers maintained a continuous, drift-free epoch reference for more than two millennia.&lt;/p&gt;

&lt;p&gt;When I first started looking at BaZi, or Four Pillars records, I expected paragraphs of free-form symbolic prose. Instead, I found a compact record of four paired labels: year, month, day, and hour. Each pillar consists of one stem-branch pair drawn from two repeating counters. There are 10 Heavenly Stems and 12 Earthly Branches. If every stem could pair with every branch independently, you would get 120 combinations. But the traditional algorithm steps both lists simultaneously. Starting from Jia-Zi, the sequence proceeds to Yi-Chou, then Bing-Yin, and so on. Because both counters step in tandem, only 60 distinct pairings occur before the system returns to Jia-Zi.&lt;/p&gt;

&lt;p&gt;In code, this periodic state can be represented with a single integer scalar n. The stem index is evaluated as n mod 10, and the branch index as n mod 12. Incrementing n by 1 advances to the next paired symbol, while incrementing n by 60 returns the system to its starting pair. It operates like two clocks with different period lengths sharing a single step counter.&lt;/p&gt;

&lt;p&gt;This shared encoding makes cycle positions directly comparable across time scales, but those four pillars are not one giant clock ticking at a uniform speed. Each unit uses its own rules to find its current index. A day is divided by the hour branch into 12 two-hour intervals, such as Zi around 11 p.m. to 1 a.m. The hour stem is not an isolated counter looked up from clock time alone; its assignment is linked directly to the day stem.&lt;/p&gt;

&lt;p&gt;Beyond the initial birth chart, the system extends into longer ten-year phases derived from the month pillar. These decade pillars advance through the stem-branch sequence every ten years. This invites a state-machine analogy where the birth chart acts as the initial state, the decade pillar changes at a calculated boundary, and current calendar cycles provide additional inputs. However, this state machine only tells us how labels transition under defined rules; it does not establish physical causation.&lt;/p&gt;

&lt;p&gt;I am still unsure about which time-normalization convention is most faithful to historical practice: civil clock time, local mean solar time, or apparent solar time. What stands out to me as a data engineer is the structural elegance. Integer arithmetic alone maintains a periodic index without requiring floating-point adjustments.&lt;/p&gt;

&lt;p&gt;Not medical, legal, or financial advice. A cultural and cognitive framework for self-reflection; outputs vary by individual.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>algorithms</category>
      <category>history</category>
      <category>culture</category>
    </item>
    <item>
      <title>I Modeled BaZi as a State Machine</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:38:27 +0000</pubDate>
      <link>https://dev.to/davidwebb915/i-modeled-bazi-as-a-state-machine-571f</link>
      <guid>https://dev.to/davidwebb915/i-modeled-bazi-as-a-state-machine-571f</guid>
      <description>&lt;p&gt;What happens when a birth time lands in the hour a solar term changes? A 30-minute error can swap an entire pillar.&lt;/p&gt;

&lt;p&gt;I spent the past year building a computation engine for this system and reverse-engineering its rules. I don't treat it as a mystical practice. I think of it as a modeling system written in ancient language. Once I translated the terms into engineering concepts, the underlying structures started to look familiar.&lt;/p&gt;

&lt;h2&gt;
  
  
  The graph under the classical terms
&lt;/h2&gt;

&lt;p&gt;The implementation starts with a directed graph of five nodes: Wood, Fire, Earth, Metal, and Water. Its edges have three action types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fuels&lt;/code&gt;: A's energy pattern nourishes B&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shapes&lt;/code&gt;: A's pattern constrains and molds B&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;drains&lt;/code&gt;: A's pattern draws energy out of B&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every classical rule maps onto the graph. The traditional vitality states a node can occupy are canonical network states under an external drive signal. Once the graph is in place, the rest is graph traversal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the ten roles do
&lt;/h2&gt;

&lt;p&gt;The system defines ten archetypal roles, ten orthogonal dimensions for observing personality. I use a role matrix: for each input, every dimension gets an activation weight. In the product, we render them as interactive archetype animals because people parse the animals faster than abstract factors.&lt;/p&gt;

&lt;p&gt;The roles describe dynamics. One may fuel or drain you, and pressure changes which one activates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time has two layers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Long phases: The lifespan is segmented into extended phases, each with a different dominant graph configuration.&lt;/li&gt;
&lt;li&gt;Annual drive: Each year contributes an external input that perturbs the network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Put together, these form what I call an energy-weather layer, a rolling state simulation over time. The weather comparison only goes so far. A forecast can say rain may be coming; packing an umbrella is your decision. The system leaves that decision alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The day node is the reference
&lt;/h2&gt;

&lt;p&gt;The baseline activation state is the day node among the four time pillars. I measure every other node relative to it using the graph's edges. That reference frame keeps the calculation from turning into interpretive mush.&lt;/p&gt;

&lt;h2&gt;
  
  
  The boundary cases take the work
&lt;/h2&gt;

&lt;p&gt;Most of the engineering effort went into handling boundaries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;True solar time: Clock time follows your time zone's central meridian. Solar position at the actual birthplace can differ by tens of minutes. Usually that's harmless. Near an hour when a solar term changes, a 30-minute error can swap an entire pillar.&lt;/li&gt;
&lt;li&gt;Daylight saving: North America changed its DST rules in 2007, and the southern hemisphere shifts in the opposite direction. I delegate conversions to the platform's IANA time-zone data rather than maintaining a table by hand.&lt;/li&gt;
&lt;li&gt;Half-hour offsets: Adelaide is UTC+9:30; Kolkata is UTC+5:30. Any conversion that assumes whole-hour offsets breaks.&lt;/li&gt;
&lt;li&gt;Year and month boundaries: Lichun in early February sets the year boundary. Lunar New Year doesn't. Month boundaries follow solar terms, not calendar days. Two schools disagree about the day boundary at 23:00, so I exposed that choice as a config flag. It's a schema decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The part I'm still unsure about
&lt;/h2&gt;

&lt;p&gt;My working theory is that these systems began as observation-based models, recording patterns in human behavior in its time and place, using the formal language available then. I suspect the mystical framing accumulated in later centuries. I can't establish that history from the implementation, so I keep it as a theory.&lt;/p&gt;

&lt;p&gt;We show the computed pattern from someone's birth data. It doesn't tell them who they are.&lt;/p&gt;

&lt;p&gt;The full derivation is documented on the AcrosStar blog.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: this post is not medical, legal, or financial advice. The system described is a cultural and cognitive framework for self-observation; outputs vary by individual.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>algorithms</category>
      <category>history</category>
      <category>culture</category>
    </item>
    <item>
      <title>Launching on Product Hunt this Tuesday — the build-in-public log of a decision tool</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Sun, 27 Sep 2026 12:10:07 +0000</pubDate>
      <link>https://dev.to/davidwebb915/launching-on-product-hunt-this-tuesday-the-build-in-public-log-of-a-decision-tool-1g71</link>
      <guid>https://dev.to/davidwebb915/launching-on-product-hunt-this-tuesday-the-build-in-public-log-of-a-decision-tool-1g71</guid>
      <description>&lt;p&gt;Six months ago I started logging my own decisions after noticing I kept repeating the same mistakes — not from lack of information, but from never looking at &lt;em&gt;how&lt;/em&gt; I decide. That habit became &lt;strong&gt;TangoEra&lt;/strong&gt;: a quiz that maps your decision profile (how you weigh risk, how you handle trade-offs, where you stall) plus a decision log that turns your real choices into data, so the profile sharpens with every decision you record.&lt;/p&gt;

&lt;p&gt;We're in free beta: 3 Starter report seats open every day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomorrow (Wednesday) we're launching TangoEra on Product Hunt.&lt;/strong&gt; If decision fatigue is your thing, we'd genuinely love your support — and your honest feedback.&lt;/p&gt;

&lt;p&gt;Beta seats: &lt;a href="https://tangoera.com/beta-tester" rel="noopener noreferrer"&gt;https://tangoera.com/beta-tester&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What decision are you procrastinating on right now?&lt;/p&gt;

</description>
      <category>producthunt</category>
      <category>buildinpublic</category>
      <category>saas</category>
      <category>productivity</category>
    </item>
    <item>
      <title>We Built a Decision-Making Tool: 30 Days of Funnel Data</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Fri, 25 Sep 2026 16:32:22 +0000</pubDate>
      <link>https://dev.to/davidwebb915/we-built-a-decision-making-tool-30-days-of-funnel-data-4883</link>
      <guid>https://dev.to/davidwebb915/we-built-a-decision-making-tool-30-days-of-funnel-data-4883</guid>
      <description>&lt;p&gt;For the last 30 days we've been building &lt;strong&gt;TangoEra&lt;/strong&gt; — a decision-support tool that turns a short structured assessment into a visual "decision profile": how you weigh risk, time horizons, compensation vs. growth, and the other dimensions that actually drive your big calls.&lt;/p&gt;

&lt;p&gt;This is the honest build-in-public report: what we shipped, what the funnel data says, and the uncomfortable lesson week one taught us.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built first
&lt;/h2&gt;

&lt;p&gt;We went against the usual indie playbook. Instead of shipping fast and instrumenting later, we spent the early weeks on the unglamorous layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A 7-dimension assessment (quiz) with per-step telemetry, so we can see exactly where people drop off&lt;/li&gt;
&lt;li&gt;A results/paywall flow with UTM-tagged sources (blog CTA, embedded quiz, result page) so every channel is separately measurable&lt;/li&gt;
&lt;li&gt;Three lead-capture pages wired into the same event pipeline&lt;/li&gt;
&lt;li&gt;An analytics-events worker streaming into a queryable dataset (we learned the hard way that eventually-consistent key-value lists lie to you at small volume — direct SQL on the event table is the only read we trust now)&lt;/li&gt;
&lt;li&gt;An SEO/content layer: a decision-framework blog, Open Graph cards, hreflang for six languages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By launch-plus-one-week the engineering checklist was 79/79 green, 20 Pinterest pins were scheduled, and a 232-URL audit of the site came back clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then we opened the window and measured
&lt;/h2&gt;

&lt;p&gt;We gave the funnel a clean measurement window (T0 + ~28 hours of stable, fully instrumented production) and pulled the authoritative dataset directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;External funnel events: 0.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not near-zero. Zero. The daily event ledger confirmed it: every single event in the window traced back to our own smoke tests and probes. No quiz completions, no CTA clicks, no leads — from real visitors — in the entire window. Site UVs sat in the "hundreds per day" range on a good day, and the distribution channels we'd stood up (Pinterest, directories) hadn't produced their first outbound click yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson: we built a product and forgot a distribution engine
&lt;/h2&gt;

&lt;p&gt;Here's the part that stung. The conversion copy was fine. The instrumentation was fine. The drop-off analysis we wanted to run? &lt;strong&gt;Statistically impossible&lt;/strong&gt; — you can't analyze a funnel with &lt;code&gt;n = 0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the sample size is zero, every A/B test, every headline tweak, every paywall experiment is theater. We had optimized a machine that nothing was flowing through.&lt;/p&gt;

&lt;p&gt;So week two got re-planned around a single sentence from our review doc: &lt;strong&gt;the funnel experiment is short on traffic, not copy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distribution became a first-class workstream, not an afterthought — one owner, daily cadence, its own backlog&lt;/li&gt;
&lt;li&gt;Content moved from "support docs for SEO" to an actual channel: decision-framework articles targeting long-tail queries, each with a hard CTA into the quiz&lt;/li&gt;
&lt;li&gt;We started measuring channels individually (separate UTM sources per surface) so that when traffic &lt;em&gt;does&lt;/em&gt; arrive, we already know which door it came through&lt;/li&gt;
&lt;li&gt;We set explicit kill criteria: a channel that can't produce outbound clicks in 30 days gets cut, no sentiment involved&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the data actually helped
&lt;/h2&gt;

&lt;p&gt;Zero is a useful number. Because the instrumentation was solid &lt;em&gt;before&lt;/em&gt; traffic existed, we can state the null result with confidence instead of wondering if the analytics are lying. We also caught a real bug class early — our first read of the "zero" was itself wrong (a stale list API masked 11 probe events that &lt;em&gt;should&lt;/em&gt; have been there), which taught us to verify reads with strong-consistency queries before making decisions on them.&lt;/p&gt;

&lt;p&gt;That meta-lesson — instrument the instrument — turned out to be worth the week by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ship the distribution backlog (channels already live: Pinterest, startup directories, guest posts on decision-making content)&lt;/li&gt;
&lt;li&gt;Re-open the funnel window with the same rigor once inbound traffic crosses a threshold where numbers mean something&lt;/li&gt;
&lt;li&gt;Publish the week-2 review the same way: real data, no varnish&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building something too and you've also stared at a dashboard full of zeros — you're not behind, you're just at the step most people skip over. The fix isn't more features. It's more doors.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow along — we're posting these reviews weekly. And if decision-making content is your thing, the &lt;a href="https://tangoera.com/quiz?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=w1-report" rel="noopener noreferrer"&gt;TangoEra quiz&lt;/a&gt; is free: three questions, one visual decision profile.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>indiehackers</category>
      <category>startup</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building an AI Tarot Decision Engine: Turning Seven Dimensions into Structured Reports</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Tue, 15 Sep 2026 12:39:58 +0000</pubDate>
      <link>https://dev.to/davidwebb915/building-an-ai-tarot-decision-engine-turning-seven-dimensions-into-structured-reports-2p1c</link>
      <guid>https://dev.to/davidwebb915/building-an-ai-tarot-decision-engine-turning-seven-dimensions-into-structured-reports-2p1c</guid>
      <description>&lt;p&gt;Some weeks, I ship everything on my list. Other weeks, I stare at a half-written email for two hours and call it a day. I used to blame focus, sleep, or motivation—until I started treating my decisions as data instead of feelings.&lt;br&gt;
That habit became a product: an AI-assisted &lt;strong&gt;decision report engine&lt;/strong&gt; that presents structured analysis as a tarot-style reading. This post is about the model underneath—no mysticism required—and what building it taught me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The observation
&lt;/h2&gt;

&lt;p&gt;For years, whenever I faced a decision I was avoiding, I’d pull tarot cards. Not to predict the future, but because a spread &lt;em&gt;forces&lt;/em&gt; you to look at a situation from angles you’d rather skip. Positions like “what you fear” or “what you refuse to see” are basically ready-made journal prompts.&lt;br&gt;
I realized I kept asking the same seven questions. So I wrote them down, scored them, and mapped them onto continuous dimensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The seven dimensions
&lt;/h2&gt;

&lt;p&gt;Every decision report reduces the situation to seven indices on a 0–100 scale. In practice, they’re calibrated to an observed 12–88 band, because humans rarely live at the extremes:&lt;br&gt;
| Dim | Name | What it measures |&lt;br&gt;
|---|---|---|&lt;br&gt;
| R | Risk appetite | tolerance for irreversible downside |&lt;br&gt;
| I | Intuitive synthesis | how much you trust pattern-recognition vs. explicit reasoning |&lt;br&gt;
| P | Temporal patience | willingness to wait for compounding |&lt;br&gt;
| S | Social dynamic | how much alignment/consensus you need |&lt;br&gt;
| A | Action bias | commit-fast vs. deliberate |&lt;br&gt;
| Rf | Reflection depth | tendency to re-analyze |&lt;br&gt;
| St | Stability | preference for known structures over flux |&lt;br&gt;
A quiz (about 3 minutes) scores the &lt;em&gt;question&lt;/em&gt;, not the person. The result is a seven-dimension radar, plus the two or three tensions hiding inside it. For example, high reflection and low action under load reads as “decision fatigue wearing a costume.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Archetypes as geometry
&lt;/h2&gt;

&lt;p&gt;Ten recurring vectors emerged from the calibration data. Each is just a shape across the seven axes:&lt;br&gt;
| Archetype | R | I | P | S | A | Rf | St | Failure mode |&lt;br&gt;
|---|---|---|---|---|---|---|---|---|&lt;br&gt;
| Alchemist | 66 | 22 | 58 | 30 | 64 | 44 | 34 | solitary conviction without alignment |&lt;br&gt;
| Sage | 40 | 78 | 74 | 52 | 20 | 72 | 56 | over-analysis; missed asymmetric upside |&lt;br&gt;
| Challenger | 76 | 40 | 28 | 44 | 84 | 30 | 32 | hyperactive interference; burn rate |&lt;br&gt;
| Builder | 44 | 62 | 74 | 52 | 50 | 58 | 76 | rigidity past optimal pivot points |&lt;br&gt;
| Creator | 60 | 30 | 30 | 42 | 76 | 30 | 28 | draft overload; consolidation lag |&lt;br&gt;
| Opportunist | 74 | 50 | 34 | 46 | 72 | 34 | 30 | shallow commitment; option chasing |&lt;br&gt;
| Architect | 38 | 78 | 76 | 50 | 58 | 74 | 76 | inelasticity in fast-changing terrain |&lt;br&gt;
| Leader | 56 | 56 | 64 | 82 | 72 | 52 | 64 | high consensus load; diluted velocity |&lt;br&gt;
| Maverick | 76 | 40 | 30 | 22 | 78 | 30 | 26 | key-person isolation |&lt;br&gt;
| Competitor | 70 | 50 | 40 | 58 | 78 | 40 | 48 | scoreboard fixation |&lt;br&gt;
These aren’t personality verdicts; they’re &lt;em&gt;operating modes&lt;/em&gt;. The same person can switch vectors depending on the domain—work, relationships, or money.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engineering notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quiz-gated queue.&lt;/strong&gt; The free beta allows 3 readings/day, with the quiz as a gate so the queue stays human-scale and bots can’t burn the LLM budget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The LLM does framing, not fortune-telling.&lt;/strong&gt; It turns the scored dimensions into tensions and next steps; the tarot layer is a presentation skin over a structured report.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Honest statistics.&lt;/strong&gt; The birth-timestamp mapping that seeds the archetype guess has a weak statistical correlation. The product says so in the UI and in the copy. Users trust it more, not less, for that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naming is the intervention.&lt;/strong&gt; The most common feedback is: “I froze less once the pattern had a name.” A labeled failure mode is fixable; unnamed anxiety isn’t.
## What I’d do differently&lt;/li&gt;
&lt;li&gt;Ship the radar before the tarot skin. The mystical frame is great for reach, but retention comes from the structured report.&lt;/li&gt;
&lt;li&gt;Score the &lt;em&gt;question&lt;/em&gt; first, person second. Question-scoring generalizes; profiling feels invasive.&lt;/li&gt;
&lt;li&gt;Ship with the disclosure. Every A/B test said honesty converts better than mystique.
If you’re stuck in a decision loop, try tracking one dimension—risk or action—for a week. Naming the pattern is half the fix.
---
&lt;em&gt;I’m building this in public—the engine is in free beta (&lt;a href="https://www.tangoera.com" rel="noopener noreferrer"&gt;tangoera.com&lt;/a&gt;), and the longer build log lives on &lt;a href="https://www.indiehackers.com/davidwebb915" rel="noopener noreferrer"&gt;Indie Hackers&lt;/a&gt;. Happy to answer questions about the calibration data or quiz design.&lt;/em&gt;
&amp;lt;!--END--&amp;gt;
tokens used&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>startup</category>
      <category>productivity</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Building an AI Tarot Decision Engine: Why I Used Tarot as a Framing Layer (and What the 7-Dimension Radar Actually Models)</title>
      <dc:creator>David Webb</dc:creator>
      <pubDate>Tue, 15 Sep 2026 12:39:10 +0000</pubDate>
      <link>https://dev.to/davidwebb915/building-an-ai-tarot-decision-engine-why-i-used-tarot-as-a-framing-layer-and-what-the-7-dimension-2174</link>
      <guid>https://dev.to/davidwebb915/building-an-ai-tarot-decision-engine-why-i-used-tarot-as-a-framing-layer-and-what-the-7-dimension-2174</guid>
      <description>&lt;p&gt;A few months ago I caught myself doing the thing every solo founder does: re-reading the same pros-and-cons list for the fourth time, hoping the answer would change. I wasn't lacking information. I was stuck in a loop — re-analyzing instead of committing.&lt;/p&gt;

&lt;p&gt;That loop is what I ended up building a product around. TangoEra is an AI-powered decision report tool, currently in a free beta. This post is the engineering story: why tarot, what the engine actually computes, and the mistakes I made along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why tarot is a framing layer, not a prediction layer
&lt;/h2&gt;

&lt;p&gt;The obvious objection: "AI tarot? So it predicts the future?" No — and this distinction is the whole design.&lt;/p&gt;

&lt;p&gt;Tarot survived for centuries not because it predicts outcomes, but because a card spread is a &lt;strong&gt;forced perspective machine&lt;/strong&gt;. The Fool, the Tower, the Two of Swords — each card is a lens that drags your thinking onto an axis you were avoiding. When you draw "restraint" on a day you're fired up to quit your job, the value isn't mystical. It's a structured interruption of your default narrative.&lt;/p&gt;

&lt;p&gt;So the product framing is: the AI generates an archetype profile and a decision report, and the tarot visual layer is the interface that makes people &lt;em&gt;slow down and actually read the analysis&lt;/em&gt; instead of skimming bullet points. The cards are UI, not oracle. Every report carries the honest disclaimer: this is a self-reflection and decision-support tool based on statistical weak correlation, not prediction, not advice.&lt;/p&gt;

&lt;p&gt;I think of it as putting a decision matrix inside a story people will finish.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engine: archetype vectors, not a black box LLM
&lt;/h2&gt;

&lt;p&gt;Under the hood there are two layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — the calibration engine.&lt;/strong&gt; We model each user's decision style as a vector over seven continuous dimensions: risk appetite, intuitive synthesis, temporal patience, social dynamic, action bias, reflection depth, and stability. Scores live on a 0–100 index, but real profiles only occupy an observed 12–88 band, which matters for normalization.&lt;/p&gt;

&lt;p&gt;We maintain 10 prototype archetype vectors — the Alchemist, the Sage, the Challenger, the Builder, the Architect, the Maverick, and so on. Each archetype is a distinct geometric shape across the seven dimensions, with an operational strength and a primary failure mode. The Sage scores high on reflection and patience, low on action bias — brilliant at verification, prone to missing asymmetric upside through over-analysis. The Challenger is the mirror image. When a user's quiz answers come in, we project them onto this prototype space (nearest-shape matching with soft membership, not hard classification) rather than asking an LLM to freestyle a personality type. That keeps output stable, testable, and cheap to regression-test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — the report radar.&lt;/strong&gt; The seven dimensions the user &lt;em&gt;sees&lt;/em&gt; in their report are translated into decision-facing axes: clarity, risk appetite, timing, relationships, resources, blind spots, and next move. This is where the tarot cards bind to computed values — a card is selected because a dimension score (plus its distance from the archetype's own baseline) triggers it, not at random.&lt;/p&gt;

&lt;p&gt;The tension rendering is the part I'm proudest of. A report that says "you're high risk, high reflection" is useless. A report that says &lt;strong&gt;"your reflection score is 38 points above your action score — that gap is your frozen-week pattern, and here is the specific next step sized to close it"&lt;/strong&gt; is a tool. Reports render two sections: &lt;em&gt;tensions&lt;/em&gt; (pairs of dimensions that are far apart and therefore in internal conflict) and &lt;em&gt;next steps&lt;/em&gt; (one concrete, low-commitment action per tension). One next step, not five. Choice overload is the disease; a report with five action items is just more of it.&lt;/p&gt;

&lt;p&gt;This borrows directly from the decision-matrix tradition (Stuart Pugh's concept-selection matrices): score dimensions, weight by importance, let arithmetic surface structure instead of opinions. Our quiz is essentially a weighted scoring instrument wearing a tarot costume.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3-minute quiz as a queue gate
&lt;/h2&gt;

&lt;p&gt;The quiz exists for two reasons. First, calibration: we need ~20 weighted answers to place you in the seven-dimensional space with enough confidence that the radar isn't noise. Second — and this is a product decision, not a technical one — it gates the daily report queue.&lt;/p&gt;

&lt;p&gt;We generate a limited number of full AI-written reports per day, with a daily 3 seats for the free beta. The quiz is the gate: it filters out drive-by traffic and makes sure every generated report gets a human who actually engaged with their inputs. As a solo dev, this isn't artificial scarcity — it's capacity management. Each report is reviewed for template drift, and human-scale volume is the only way I can catch the LLM quietly going off-script. If you've ever run a generative feature unattended, you know the failure mode: output slowly drifts and nobody notices for weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The birth-timestamp-to-dimension mapping is a &lt;strong&gt;weak statistical correlation&lt;/strong&gt;, not science. It's a vocabulary generator, not a verdict. I'm upfront about this in-product.&lt;/li&gt;
&lt;li&gt;Quiz self-reporting has all the usual biases. People answer who they want to be on Monday morning.&lt;/li&gt;
&lt;li&gt;The report is a mirror. If a user wants certainty, no tool sells that honestly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solo-dev lessons
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Constrain the LLM with geometry.&lt;/strong&gt; Let the model &lt;em&gt;write prose inside a computed skeleton&lt;/em&gt; (archetype vectors, tension pairs, threshold rules), never invent the skeleton. Testability went from "vibes" to unit tests on vectors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-scale queues are a feature.&lt;/strong&gt; A daily cap forces review loops, which catch drift, which builds trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Own the weird framing.&lt;/strong&gt; Tarot gets attention that "decision support SaaS" never would, and the card constraint genuinely improves the writing — forced metaphors beat blank pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building something similar, steal the pattern: compute hard, narrate softly, and always label the correlation honestly.&lt;/p&gt;

&lt;p&gt;Try the free beta: &lt;a href="https://tangoera.com/?utm_source=devto" rel="noopener noreferrer"&gt;https://tangoera.com/?utm_source=devto&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
