<?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: Tim</title>
    <description>The latest articles on DEV Community by Tim (@winsznx).</description>
    <link>https://dev.to/winsznx</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%2F4048419%2F53ed48f7-482b-432c-8c18-db4997eaa41b.png</url>
      <title>DEV Community: Tim</title>
      <link>https://dev.to/winsznx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/winsznx"/>
    <language>en</language>
    <item>
      <title>I tried to teach a model my videographer friend's eye in one night. 156 sets of picks said no.</title>
      <dc:creator>Tim</dc:creator>
      <pubDate>Mon, 05 Oct 2026 06:10:44 +0000</pubDate>
      <link>https://dev.to/winsznx/i-tried-to-teach-a-model-my-videographer-friends-eye-in-one-night-156-sets-of-picks-said-no-5e6k</link>
      <guid>https://dev.to/winsznx/i-tried-to-teach-a-model-my-videographer-friends-eye-in-one-night-156-sets-of-picks-said-no-5e6k</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;My friend is a videographer who also builds websites. Like most people who shoot events, she comes home with hours of footage and spends the next day watching it to find the few minutes she'll actually use.&lt;/p&gt;

&lt;p&gt;Photographers have had tools that learn how they cull for years. I couldn't find a video tool that learns which clips an editor keeps. So I built &lt;strong&gt;Winnow&lt;/strong&gt;: a local Python CLI that cuts footage into clips, embeds them with an open vision model, learns from an editor's own keep/skip picks, ranks the rest of the shoot, and exports markers for Resolve, Final Cut and Premiere. It never hides or deletes a clip. It only changes the order you review in.&lt;/p&gt;

&lt;p&gt;Then I tried to prove it works. It didn't, and that turned out to be the most useful part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Her footage didn't arrive before the deadline, so I tested on &lt;strong&gt;SumMe&lt;/strong&gt; (Gygli et al., ECCV 2014): 25 public videos where 15 to 18 people each marked which moments they'd keep. Each person stands in for one editor with their own taste.&lt;/p&gt;

&lt;p&gt;The setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cut every video into 2-second segments.&lt;/li&gt;
&lt;li&gt;For each editor, train on their picks in the first 25% of the video (by time), then rank the rest.&lt;/li&gt;
&lt;li&gt;Score: what share of that editor's own later picks show up in the first 20% of the ranked list.&lt;/li&gt;
&lt;li&gt;I wrote the pass/fail rules down before running anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;390 editor-video pairs existed. 156 were usable, 234 were skipped (mostly too few picks in the first quarter).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Recall &lt;a class="mentioned-user" href="https://dev.to/10"&gt;@10&lt;/a&gt;%&lt;/th&gt;
&lt;th&gt;Recall @20%&lt;/th&gt;
&lt;th&gt;Recall @30%&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Watch in order&lt;/td&gt;
&lt;td&gt;0.16&lt;/td&gt;
&lt;td&gt;0.27&lt;/td&gt;
&lt;td&gt;0.35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic model (other videos, no labels from this one)&lt;/td&gt;
&lt;td&gt;0.16&lt;/td&gt;
&lt;td&gt;0.30&lt;/td&gt;
&lt;td&gt;0.41&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal model (TabPFN on this editor's early picks)&lt;/td&gt;
&lt;td&gt;0.16&lt;/td&gt;
&lt;td&gt;0.26&lt;/td&gt;
&lt;td&gt;0.38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid (generic + personal)&lt;/td&gt;
&lt;td&gt;0.17&lt;/td&gt;
&lt;td&gt;0.27&lt;/td&gt;
&lt;td&gt;0.38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logistic regression, same features&lt;/td&gt;
&lt;td&gt;0.18&lt;/td&gt;
&lt;td&gt;0.30&lt;/td&gt;
&lt;td&gt;0.38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Similarity to early picks&lt;/td&gt;
&lt;td&gt;0.10&lt;/td&gt;
&lt;td&gt;0.18&lt;/td&gt;
&lt;td&gt;0.27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal model, shuffled labels (control)&lt;/td&gt;
&lt;td&gt;0.13&lt;/td&gt;
&lt;td&gt;0.21&lt;/td&gt;
&lt;td&gt;0.31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crowd oracle (not deployable)&lt;/td&gt;
&lt;td&gt;0.39&lt;/td&gt;
&lt;td&gt;0.56&lt;/td&gt;
&lt;td&gt;0.67&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pre-registered verdicts, all &lt;strong&gt;NULL&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Does a person's own picks add value over a generic model?&lt;/strong&gt; Hybrid minus generic: -0.033, 95% CI [-0.075, +0.011]. No.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it beat similarity search by 10+ points?&lt;/strong&gt; +0.080, 95% CI [+0.042, +0.119]. Better, but under the bar I set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does a generic model beat just watching in order?&lt;/strong&gt; +0.032, 95% CI [-0.019, +0.081]. Not clearly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/winsznx" rel="noopener noreferrer"&gt;
        winsznx
      &lt;/a&gt; / &lt;a href="https://github.com/winsznx/winnow" rel="noopener noreferrer"&gt;
        winnow
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Learns which clips an editor keeps and ranks the rest, locally, with open models. V0 benchmark on SumMe: honest null.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Winnow&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Winnow learns which clips an editor keeps from their own early picks, then ranks the rest of a shoot the way they would, locally, with open models (SigLIP embeddings plus TabPFN v2). It is a Python CLI. It exports ranked markers for Resolve, Final Cut and Premiere, and it never hides or deletes a clip.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Status: tonight's numbers are from SumMe users, not the friend&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;The videographer this was built for could not share footage in time. The results below come from SumMe (Gygli et al., ECCV 2014), 25 public videos with 15 to 18 people's keep/skip summaries each. Each of those people stands in for one editor. The friend's own run comes next, through the &lt;code&gt;winnow run&lt;/code&gt; path below, which has only run on small fixtures and synthetic clips so far.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Results (SumMe, run 20261005T021310Z, from results/20261005T021310Z/results.json)&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Correction: the first run (20261005T020454Z) compared against a crowd arm that…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/winsznx/winnow" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Reproduce the whole benchmark with one command: &lt;code&gt;winnow bench summe&lt;/code&gt;. For a real editor: &lt;code&gt;winnow run --clips &amp;lt;folder&amp;gt; --timeline &amp;lt;export.fcpxml&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open vision model&lt;/strong&gt; (SigLIP via open_clip) for clip embeddings, plus sharpness, motion, exposure and duration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TabPFN v2&lt;/strong&gt; (Apache 2.0) as the small-data learner, chosen because a real editor gives you dozens of picks, not thousands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A timeline parser&lt;/strong&gt; for FCPXML, Premiere XML and EDL, so an editor's finished project becomes training labels with no manual tagging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every arm on the same split&lt;/strong&gt;, a shuffled-label control, and a written pre-registration before compute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two mistakes I caught along the way, both left visible in the repo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My first "crowd" baseline used other people's labels on the exact segments being scored. That's information no real tool has, so it's kept as an oracle and replaced with a fair generic model.&lt;/li&gt;
&lt;li&gt;A random train/test split made the personal model look like a winner. It was leakage: neighbouring 2-second segments of the same shot landed on both sides. The time-ordered split is the honest one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What I learned: 25% of one video is too little signal to learn anyone's taste, and early footage doesn't look like late footage. The real test is the one I designed for: an editor's whole past projects, where every finished timeline is a full record of what she kept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;A tool that learns your taste from your client footage should never need that footage to leave your laptop. With open weights, the model trains locally, the profile is a file she owns, and rerunning costs nothing. I ran 390 editor-video pairs twice tonight without paying anyone per minute of video.&lt;/p&gt;

&lt;p&gt;Open also made the failure visible. Every arm, the control and the null are in the repo for anyone to check. A closed API would have made this experiment expensive and the result a black box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;I'll run Winnow on her real projects and add her reaction here as a dated update. Not yet verified: marker import in each editor, and &lt;code&gt;winnow run&lt;/code&gt; on real footage (so far it has only run on synthetic clips).&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of TabPFN.&lt;/strong&gt; TabPFN v2 is the core learner: each editor's early keep/skip picks become a small table (clip embeddings plus technical features), and TabPFN classifies the rest of the shoot with no training loop, which is exactly the small-data regime a single editor lives in. I ran it head to head against logistic regression on identical features and published the result: on SumMe they tied at 20% review depth. That honest comparison is in the repo, so the next person knows where TabPFN helps and where it doesn't.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>The agent gave the right answer and did the wrong thing</title>
      <dc:creator>Tim</dc:creator>
      <pubDate>Sun, 26 Jul 2026 21:47:12 +0000</pubDate>
      <link>https://dev.to/winsznx/the-agent-gave-the-right-answer-and-did-the-wrong-thing-4gmg</link>
      <guid>https://dev.to/winsznx/the-agent-gave-the-right-answer-and-did-the-wrong-thing-4gmg</guid>
      <description>&lt;h2&gt;
  
  
  The bug that passes every test &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A refund agent ships v2. A customer asks for a refund. The agent replies:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your refund of $48.20 has been issued and will appear in 3–5 business days.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is exactly what v1 said. The amount is right. The tone is right. An LLM judge scores it&lt;br&gt;
identically. A regression suite comparing outputs sees no diff. Ship it.&lt;/p&gt;

&lt;p&gt;Underneath, this happened:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v1 (approved)                          v2 (shipped)
refund.request                         refund.request
  -&amp;gt; policy.retrieve                     -&amp;gt; order.lookup
  -&amp;gt; order.lookup                        -&amp;gt; payment.refund
  -&amp;gt; fraud.check                         -&amp;gt; payment.refund      &amp;lt;- charged twice
  -&amp;gt; refund.calculate                    -&amp;gt; customer.notify
  -&amp;gt; payment.refund
  -&amp;gt; customer.notify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The refund-policy retrieval is gone. The fraud check is gone. And the payment write happened twice —&lt;br&gt;
a timeout, a retry, and the payment service's idempotency ledger recording both entries.&lt;/p&gt;

&lt;p&gt;The customer got the right sentence. The company refunded $96.40 and skipped its own compliance&lt;br&gt;
checks. Every output-based control in the pipeline was green.&lt;/p&gt;

&lt;p&gt;This is not a hypothetical. It is the failure mode that appears the moment an agent has a retry&lt;br&gt;
policy, a tool it can skip, and a prompt somebody edited on a Friday. Output evaluation is&lt;br&gt;
structurally blind to it, because the output is fine.&lt;/p&gt;

&lt;p&gt;The execution &lt;em&gt;trajectory&lt;/em&gt; is where the bug lives. And a trajectory is exactly what a distributed&lt;br&gt;
trace is.&lt;/p&gt;
&lt;h2&gt;
  
  
  FlightRules &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/winsznx/flightrules" rel="noopener noreferrer"&gt;FlightRules&lt;/a&gt; turns SigNoz traces into deterministic release&lt;br&gt;
contracts. It mines a &lt;strong&gt;trajectory contract&lt;/strong&gt; from runs a human approved, then evaluates every&lt;br&gt;
subsequent release against it, and fails the release with the traces that prove it. In CI that is&lt;br&gt;
one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;flightrules gate check &lt;span class="nt"&gt;--release&lt;/span&gt; refund-agent-v2
FAIL: This release exceeded one or more trajectory thresholds.

Findings
  MISSING_PREREQUISITE &lt;span class="o"&gt;(&lt;/span&gt;skipped_check&lt;span class="o"&gt;)&lt;/span&gt;
    fraud.check is required at least 1 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; but occurred 0 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
  DUPLICATE_SIDE_EFFECT &lt;span class="o"&gt;(&lt;/span&gt;duplicate_write&lt;span class="o"&gt;)&lt;/span&gt;
    payment.refund occurred 2 &lt;span class="nb"&gt;times&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; the contract permits at most 1 per run.

&lt;span class="nb"&gt;exit &lt;/span&gt;code: 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit code 2. The pipeline stops.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Five stages, and the interesting design decisions are all about what is &lt;em&gt;not&lt;/em&gt; allowed in each.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Instrument.&lt;/strong&gt; The demo is a refund agent and five services — policy, orders, fraud, payment,&lt;br&gt;
notification — instrumented with OpenTelemetry and exporting traces, metrics and logs over OTLP into&lt;br&gt;
SigNoz. The agent emits &lt;code&gt;gen_ai.*&lt;/code&gt; attributes for tool identity and operation, plus a small&lt;br&gt;
FlightRules namespace: &lt;code&gt;agent.side_effect&lt;/code&gt;, &lt;code&gt;agent.data_domain&lt;/code&gt;, &lt;code&gt;agent.retry.number&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;agent.idempotency.present&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It emits &lt;strong&gt;no prompt, no model output, no tool arguments, no tool results and no chain-of-thought&lt;/strong&gt;.&lt;br&gt;
That was a constraint from the start, and it turned out to be the interesting one: if trajectory&lt;br&gt;
enforcement works without any of it, the product is deployable in places prompt logging is not.&lt;br&gt;
It does work. The forbidden-key redactor &lt;em&gt;removes&lt;/em&gt; those attributes rather than replacing them with&lt;br&gt;
&lt;code&gt;[redacted]&lt;/code&gt; — a redaction marker would still record that the product collected one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Retrieve.&lt;/strong&gt; Everything goes through the SigNoz MCP Server, using the official MCP TypeScript&lt;br&gt;
SDK. The first real discovery of the project came here: &lt;strong&gt;&lt;code&gt;signoz_get_trace_details&lt;/code&gt; cannot return&lt;br&gt;
custom span attributes.&lt;/strong&gt; It returns a fixed column set. Every contract rule that reads an attribute&lt;br&gt;
would have silently seen nothing.&lt;/p&gt;

&lt;p&gt;The path that works is &lt;code&gt;signoz_execute_builder_query&lt;/code&gt; with &lt;code&gt;requestType: "raw"&lt;/code&gt; and &lt;code&gt;selectFields&lt;/code&gt;&lt;br&gt;
declaring &lt;code&gt;fieldContext: "tag"&lt;/code&gt;. There is a further asymmetry that costs an afternoon if you meet it&lt;br&gt;
by accident: discovery tools accept &lt;code&gt;"attribute"&lt;/code&gt; as an alias for &lt;code&gt;"tag"&lt;/code&gt;, but &lt;code&gt;selectFields&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;groupBy&lt;/code&gt; require &lt;code&gt;"tag"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Reconstruct.&lt;/strong&gt; Spans become a causal graph, then a canonical form, then a fingerprint. Volatile&lt;br&gt;
identifiers are normalised out, so two runs of the same behaviour with different order IDs produce&lt;br&gt;
the same fingerprint. Identical traces produce identical fingerprints — asserted with property tests&lt;br&gt;
over reordered input, reordered rules and reordered attribute keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Mine and contract.&lt;/strong&gt; Twenty-five approved runs collapse into route families. The dominant family&lt;br&gt;
is proposed as approved; anything rarer is left pending for a human, because approving every family&lt;br&gt;
automatically would defeat the review step the product exists to make explicit. From the approved&lt;br&gt;
family the miner proposes a contract in a small YAML DSL with eleven rule types — required spans,&lt;br&gt;
forbidden spans, cardinality, required edges, required ancestry, forbidden paths, attribute&lt;br&gt;
constraints, allowed values, retry budgets, approved routes, numeric budgets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Evaluate and gate.&lt;/strong&gt; A pure function over a graph and a contract. Then aggregation across a&lt;br&gt;
release, then a gate decision, then a process exit code.&lt;/p&gt;
&lt;h2&gt;
  
  
  The determinism boundary &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The single decision everything else follows from: &lt;strong&gt;no model, no clock, no network and no randomness&lt;br&gt;
participates in a pass/fail.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A model may explain a violation to a human. It may never decide whether a rule passed. The&lt;br&gt;
consequences show up in odd places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The evaluator reads no clock. The caller injects &lt;code&gt;completedAt&lt;/code&gt;, so a timestamp cannot enter the
hashed region and two evaluations of the same evidence are byte-identical.&lt;/li&gt;
&lt;li&gt;Route similarity is a weighted Jaccard ratio compared by &lt;strong&gt;exact integer cross-multiplication&lt;/strong&gt;,
never by dividing into a double. A release decision must not depend on binary rounding of a
threshold the author wrote in decimal.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;evaluateRunSafely&lt;/code&gt; is the one place an internal error becomes a status, and it structurally
cannot produce &lt;code&gt;pass&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one matters more than it sounds. A release gate that can return green after an internal&lt;br&gt;
error is worse than no gate, because it launders a crash into a permission.&lt;/p&gt;
&lt;h2&gt;
  
  
  Absence is not a value &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The subtlest correctness problem in the whole product is what to do when the evidence is missing.&lt;/p&gt;

&lt;p&gt;Consider &lt;code&gt;agent.idempotency.present&lt;/code&gt; on a payment write. If the attribute is absent, has the rule&lt;br&gt;
&lt;code&gt;equals: true&lt;/code&gt; passed or failed?&lt;/p&gt;

&lt;p&gt;Neither. It is undecided, and the evaluator says so — &lt;code&gt;insufficient_evidence&lt;/code&gt;, and the run reports&lt;br&gt;
&lt;code&gt;insufficient_data&lt;/code&gt; rather than &lt;code&gt;pass&lt;/code&gt;. A contract that could not be checked has not been satisfied.&lt;/p&gt;

&lt;p&gt;But that answer is wrong for a different operator. &lt;code&gt;not_equals: admin&lt;/code&gt; over an absent attribute is a&lt;br&gt;
genuine &lt;strong&gt;pass&lt;/strong&gt;: the span demonstrably does not carry the forbidden value. And &lt;code&gt;exists&lt;/code&gt; over an&lt;br&gt;
absent attribute is a genuine &lt;strong&gt;violation&lt;/strong&gt;: the rule asks for the attribute itself.&lt;/p&gt;

&lt;p&gt;So absence is resolved per operator. Defaulting it either way breaks something real: default to pass&lt;br&gt;
and an uninstrumented service satisfies &lt;code&gt;refund-must-be-idempotent&lt;/code&gt;; default to violation and a&lt;br&gt;
release is punished for a telemetry gap.&lt;/p&gt;

&lt;p&gt;This paid for itself. SigNoz returns a non-string tag as &lt;code&gt;null&lt;/code&gt; — with no error and no warning — when&lt;br&gt;
a query omits its &lt;code&gt;dataType&lt;/code&gt;. A rule keyed on &lt;code&gt;agent.idempotency.present&lt;/code&gt; would have read a missing&lt;br&gt;
attribute from a query that reported success. It was caught only because absence produces&lt;br&gt;
insufficient evidence instead of a pass. Had the evaluator defaulted to green, that would have been&lt;br&gt;
a green release built on a query that returned nothing.&lt;/p&gt;
&lt;h2&gt;
  
  
  The hardest scoping decision &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A client span whose server span was never exported — an aborted request, a service that died&lt;br&gt;
mid-call — makes that span's subtree unobservable.&lt;/p&gt;

&lt;p&gt;The tempting move is to mark the whole trace incomplete and refuse to conclude anything from&lt;br&gt;
absence. It is safe-looking and it destroys the product: the canary's genuinely missing fraud check&lt;br&gt;
becomes "insufficient evidence" and the gate goes green on the exact release it exists to catch.&lt;/p&gt;

&lt;p&gt;The opposite move is to treat the trace as complete. Then an aborted request looks like a skipped&lt;br&gt;
step, and the gate fails releases for network weather.&lt;/p&gt;

&lt;p&gt;The answer is to scope the uncertainty to the one span. The trace stays &lt;code&gt;complete&lt;/code&gt;, because the&lt;br&gt;
route it evidences is fully determined by the client span. The rules anchored on that span check for&lt;br&gt;
its unobservable subtree individually and report &lt;code&gt;unobservable_subtree&lt;/code&gt;. Everything else in the trace&lt;br&gt;
is decided normally.&lt;/p&gt;

&lt;p&gt;The test that pins this holds both halves at once, in a single trace: the aborted payment handler&lt;br&gt;
stays insufficient evidence &lt;em&gt;while&lt;/em&gt; the missing fraud check stays a violation.&lt;/p&gt;
&lt;h2&gt;
  
  
  SigNoz as a control surface, not a dashboard &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;FlightRules does not just read SigNoz. It writes to it: the active contract compiles into ten managed&lt;br&gt;
resources — a notification channel, four saved views, a ten-panel dashboard, and four alert rules —&lt;br&gt;
created through MCP and &lt;strong&gt;read back by identifier&lt;/strong&gt; before the register records them as synced.&lt;/p&gt;

&lt;p&gt;The read-back is not ceremony. It found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Two managed dashboards with the same name.&lt;/strong&gt; List tools key their identifier &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;uuid&lt;/code&gt; or
&lt;code&gt;ruleId&lt;/code&gt; and their name &lt;code&gt;name&lt;/code&gt; or &lt;code&gt;alert&lt;/code&gt; depending on resource type. A single &lt;code&gt;id&lt;/code&gt; lookup finds
nothing for a dashboard or an alert, which makes every already-created one look absent — so the
next sync creates a second copy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;signoz_update_view&lt;/code&gt; corrupts the tenant.&lt;/strong&gt; Whatever body it is given, it persists the composite
query as a hex-encoded byte string, after which &lt;code&gt;signoz_list_views&lt;/code&gt; returns HTTP 500 for the whole
organisation and &lt;em&gt;every&lt;/em&gt; saved view becomes unreadable — including views FlightRules did not
create. A view is now replaced by delete-then-create.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An unmatched API path returns the single-page app with HTTP 200.&lt;/strong&gt; A client that trusts a status
code reads a web page as a query result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those would have failed a test that checked the call succeeded.&lt;/p&gt;

&lt;p&gt;Alerts get the same treatment. Every managed alert was driven through firing &lt;em&gt;and&lt;/em&gt; recovery against&lt;br&gt;
the running deployment, read from SigNoz's own alert history: the violation-rate alert fired at value&lt;br&gt;
220 and recovered to &lt;code&gt;inactive&lt;/code&gt; exactly 300 seconds later, matching its configured evaluation window&lt;br&gt;
to the second.&lt;/p&gt;
&lt;h2&gt;
  
  
  What breaking it on purpose found &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The hardening phase's job was to break the product before a judge did. Twelve real defects, and the&lt;br&gt;
pattern in them is worth more than the list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A negative number is not a smaller number.&lt;/strong&gt; A span reporting &lt;code&gt;agent.retry.number: -5&lt;/code&gt; had its&lt;br&gt;
value summed into the run's retry total. Nine retries plus one mislabelled span totalled four against&lt;br&gt;
a limit of four — a clean run. A negative attempt index is not fewer retries; it is not an attempt&lt;br&gt;
index at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telemetry reaches terminals.&lt;/strong&gt; Span names, tool names and rule summaries all flow into the CLI's&lt;br&gt;
human report. A span named with &lt;code&gt;ESC [ 2 J ESC [ 1 ; 1 H PASS…&lt;/code&gt; clears the reader's screen and&lt;br&gt;
reprints the opposite verdict — into a CI log that records the escape sequence faithfully, so the&lt;br&gt;
deception survives review. Every line the CLI writes now passes through a filter applied once at the&lt;br&gt;
entry point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drift created duplicates.&lt;/strong&gt; A managed dashboard someone edited by hand failed its read-back, fell&lt;br&gt;
out of the "unchanged" path, and then took the &lt;em&gt;create&lt;/em&gt; branch — leaving a second resource beside the&lt;br&gt;
edited one. And two concurrent syncs of one agent, racing through the saved-view&lt;br&gt;
delete-and-recreate, each deleted one view and created another. Both now run under a per-agent&lt;br&gt;
PostgreSQL advisory lock covering read-register → sync → persist-register, because the register is&lt;br&gt;
what the &lt;em&gt;next&lt;/em&gt; sync plans from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The documented setup did not work.&lt;/strong&gt; The reproducibility test — a clone outside the working tree,&lt;br&gt;
volumes destroyed, from committed files only — found four defects in the exact path a judge follows.&lt;br&gt;
The best of them: SigNoz enforces a password policy on registration and states it &lt;em&gt;only in the&lt;br&gt;
rejection body&lt;/em&gt;, and the command every document recommended, &lt;code&gt;openssl rand -base64 18&lt;/code&gt;, satisfies it&lt;br&gt;
only by luck. One reproduction passed and two failed on identical instructions. Worse, both GitHub&lt;br&gt;
workflows supplied &lt;code&gt;ci-&amp;lt;run_id&amp;gt;&lt;/code&gt;, which has neither an uppercase letter nor a symbol — &lt;strong&gt;so the&lt;br&gt;
release-gate workflow could not have passed on its first run&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -sf&lt;/code&gt; had been hiding the reason, because it discards the response body on an HTTP error. The&lt;br&gt;
fix that mattered was not the password; it was making the failure legible.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the first real CI run found &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Both workflows had never executed on GitHub. Running them found three more defects, every one&lt;br&gt;
invisible on macOS and fatal on a runner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A build that succeeded and then exited 1, silently.&lt;/strong&gt; &lt;code&gt;next build&lt;/code&gt; printed&lt;br&gt;
&lt;code&gt;✓ Compiled successfully&lt;/code&gt;, then &lt;code&gt;Skipping validation of types&lt;/code&gt;, then exited &lt;code&gt;1&lt;/code&gt; forty-seven&lt;br&gt;
milliseconds later having printed nothing at all — no stack, no message, no page named.&lt;br&gt;
&lt;code&gt;NODE_OPTIONS=--trace-exit&lt;/code&gt; produced the entire diagnostic the failure had to offer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(node:2930) WARNING: Exited the environment with code 1
    at exit (node:internal/process/per_thread:241:13)
    at .../next/dist/build/type-check.js:110:17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next.js probes the filesystem for &lt;code&gt;typescript/lib/typescript.js&lt;/code&gt;. TypeScript 7 ships a native&lt;br&gt;
compiler and has no such file, so Next reports TypeScript as &lt;em&gt;missing&lt;/em&gt; while &lt;code&gt;tsc&lt;/code&gt; works perfectly.&lt;br&gt;
Off CI it quietly reinstalled TypeScript on every build; with &lt;code&gt;CI&lt;/code&gt; set it threw an error that&lt;br&gt;
&lt;code&gt;type-check.js&lt;/code&gt; discarded before calling &lt;code&gt;process.exit(1)&lt;/code&gt;, on the assumption that a worker had&lt;br&gt;
already logged it — which is false whenever &lt;code&gt;typescript.ignoreBuildErrors&lt;/code&gt; is set, because then no&lt;br&gt;
worker is spawned. It was never a Linux problem. &lt;code&gt;CI&lt;/code&gt; was the discriminator, and it reproduces on&lt;br&gt;
macOS with &lt;code&gt;CI=true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One service out of six could not resolve &lt;code&gt;host.docker.internal&lt;/code&gt;.&lt;/strong&gt; The demo topology's compose file&lt;br&gt;
declared the &lt;code&gt;host-gateway&lt;/code&gt; mapping on five services and not on the agent. Docker Desktop injects&lt;br&gt;
that name on macOS whether or not you declare it, so the omission survived sixteen phases. Linux&lt;br&gt;
does not. The agent is the only process that emits the root span and the release attribute every&lt;br&gt;
contract rule is keyed on — so with its exporter dead, the five services' server spans still arrived&lt;br&gt;
as parentless roots with no release identifier. SigNoz was healthy, ingestion returned 200, the demo&lt;br&gt;
returned a real trace identifier, and every query for a run came back empty.&lt;/p&gt;

&lt;p&gt;That one was found by changing a readiness probe to report &lt;em&gt;what SigNoz actually holds&lt;/em&gt; when a&lt;br&gt;
release never appears, instead of only that it did not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;refund-agent-v1 never became queryable after 20 attempts.
5 span(s) in the window
  services:     …fraud-service, …notification-service, …order-service, …payment-service,
                …policy-service
  span names:   customer.notify.handler, fraud.check.handler, order.lookup.handler,
                payment.refund.handler, policy.retrieve.handler
  release ids:  (unset)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five spans, one per service, no root, no agent. The search ended there.&lt;/p&gt;

&lt;p&gt;The lesson is the same one the whole project keeps relearning: &lt;strong&gt;a system that fails without naming&lt;br&gt;
a cause costs more than a system that fails loudly.&lt;/strong&gt; Both fixes are pinned by a test that was&lt;br&gt;
confirmed to fail without them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell someone starting this &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Make your verification verify.&lt;/strong&gt; &lt;code&gt;make signoz-verify&lt;/code&gt; passed against a deployment whose credential&lt;br&gt;
was still the literal string &lt;code&gt;replace-me&lt;/code&gt;. It proved the MCP server was reachable and that&lt;br&gt;
&lt;code&gt;initialize&lt;/code&gt; succeeded — but &lt;code&gt;initialize&lt;/code&gt; never presents the credential to SigNoz. Every subsequent&lt;br&gt;
tool call returned 401 against a deployment the script had just declared healthy. It now makes a real&lt;br&gt;
authenticated call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distrust your own handoff.&lt;/strong&gt; Entry verification for the final phase re-checked every figure in the&lt;br&gt;
previous session's notes instead of trusting them, and found &lt;code&gt;make verify&lt;/code&gt; exiting 2 on a file those&lt;br&gt;
notes had themselves added.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write down what the runtime did, not what the docs said.&lt;/strong&gt; Sixty-eight source-lock entries, each&lt;br&gt;
with its claim and whether the installed runtime confirmed it. Several are defects in pinned&lt;br&gt;
dependencies rather than in this product. Every one of them was found by running something.&lt;/p&gt;




&lt;p&gt;FlightRules is Apache-2.0 and reproducible from a clean clone: SigNoz deployed by Foundry from a&lt;br&gt;
committed casting file, a real agent emitting real telemetry, a contract mined from real approved&lt;br&gt;
runs, and a gate that exits 0 for the approved release and 2 for the canary.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository — &lt;a href="https://github.com/winsznx/flightrules" rel="noopener noreferrer"&gt;github.com/winsznx/flightrules&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Release — &lt;a href="https://github.com/winsznx/flightrules/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;v0.1.0&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live deployment — &lt;a href="https://flightrules-web-production.up.railway.app" rel="noopener noreferrer"&gt;flightrules-web-production.up.railway.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The canary returns the right answer. That was always the point.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>llm</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
