¿Qué es HTML realmente? (No es programación)

Si alguna vez has dicho que “programas en HTML” frente a un desarrollador senior, probablemente viste una pequeña mueca en su rostro. No te preocupes, hoy vamos a definir qué es este lenguaje y por qué es la base de todo lo que ves en internet.

La Anatomía de la Web
HTML no es un lenguaje de programación porque no maneja lógica, bucles o condicionales. Es un Lenguaje de Marcado de Hipertexto (HyperText Markup Language).

Piensa en una página web como un edificio:

HTML es el esqueleto (vigas, ladrillos y estructura).

CSS es la estética (pintura, acabados y decoración).

JavaScript es el sistema funcional (elevadores, luces y sensores).

¿Cómo funciona el “Marcado”?
El navegador es un intérprete. Cuando le envías un archivo .html, él lee etiquetas como

,

o y las traduce en elementos visuales.

El secreto de un Arquitecto Web no es solo poner etiquetas, sino usar las correctas. No es lo mismo un

con texto grande que un

. El segundo le dice a Google y a las tecnologías de asistencia: “¡Oye, esto es lo más importante de esta página!”.

Código Atómico: Tu primera etiqueta

<h1>Hola Mundo, soy John</h1>

<p>Estoy dominando los 2000 temas de HTML en <a href="https://dev.to">Dev.to</a>.</p>

HTML es el lenguaje de la Semántica. Un HTML bien escrito es legible para humanos, accesible para personas con discapacidad y amigable para los robots de búsqueda (SEO). Si el HTML está mal, el resto de la web sufrirá, sin importar cuánto CSS le pongas.

How I Built an SEC Insider Trading Alert System With 3 API Calls

How I Built an SEC Insider Trading Alert System With 3 API Calls

Every public company insider — CEOs, CFOs, board members — is legally required to report stock trades to the SEC within two business days via Form 4 filings. That’s 200+ filings per day, all public data, and it’s one of the most reliable trading signals available.

I built an API that polls EDGAR, parses the XML, and returns clean JSON. Here’s how to use it to build a real-time insider trading alert system.

Why Insider Trades Matter

Academic research consistently shows that insider purchases outperform the market by 7-10% annually. When a CEO buys $2M of their own stock on the open market, they’re putting personal money where their mouth is. That’s a signal worth tracking.

The problem: SEC EDGAR returns raw XML/SGML documents designed for regulators, not developers. Parsing Form 4 filings means dealing with accession numbers, CIK lookups, and XML namespaces.

The API handles all of that and returns structured JSON.

Quick Start: Get Recent Insider Trades

The simplest call — fetch the latest insider trades across all companies:

import requests

API_URL = "https://sec-edgar-insider-alerts-production.up.railway.app"

# Get recent insider trades (last 3 days)
response = requests.get(f"{API_URL}/sec/insider-trades", params={
    "days": 3,
    "limit": 20
})

data = response.json()
print(f"Found {data['total']} filings")

for filing in data["filings"]:
    print(f"{filing['filedDate']} | {filing['insider']['name']} | {filing['company']['name']}")

Output:

Found 847 filings
2026-03-27 | Tim Cook | Apple Inc.
2026-03-27 | Jensen Huang | NVIDIA Corp
2026-03-26 | Satya Nadella | Microsoft Corporation
...

Filter by Company Ticker

Want to monitor a specific stock? Use the company endpoint:

# Get all AAPL insider trades in the last 30 days
aapl = requests.get(f"{API_URL}/sec/insider-trades/company/AAPL", params={
    "days": 30,
    "limit": 50
})

data = aapl.json()
print(f"AAPL insider trades: {data['total']}")

for filing in data["filings"]:
    insider = filing["insider"]["name"]
    date = filing["filedDate"]
    print(f"  {date} — {insider}")

This covers 10,400+ ticker symbols — basically every public company on EDGAR.

Get Full Transaction Details

The filing detail endpoint is where it gets interesting. Each Form 4 contains the actual transaction data — shares bought/sold, price per share, and post-transaction holdings:

# Get full Form 4 details
detail = requests.get(
    f"{API_URL}/sec/insider-trades/filing/0002114927-26-000005"
)

filing = detail.json()
print(f"Insider: {filing['owner']['name']}")
print(f"Company: {filing['issuer']['name']} ({filing['issuer']['ticker']})")
print(f"Role: Director={filing['owner']['isDirector']}, Officer={filing['owner']['isOfficer']}")

for tx in filing["transactions"]:
    action = tx["codeLabel"]  # "Purchase", "Sale", "Option Exercise"
    shares = tx["shares"]
    price = tx.get("pricePerShare", "N/A")
    print(f"  {action}: {shares} shares @ ${price}")

Response includes:

  • Transaction codes: P (Purchase), S (Sale), M (Option Exercise), G (Gift), A (Grant)
  • Insider roles: Director, Officer (with title), 10% Owner
  • Both non-derivative and derivative transactions (options, warrants)
  • Post-transaction holdings so you can see their total position

Build a Simple Alert Bot

Here’s a practical example — a script that checks for large insider purchases and sends alerts:

import requests

API_URL = "https://sec-edgar-insider-alerts-production.up.railway.app"
WATCHLIST = ["AAPL", "MSFT", "GOOGL", "NVDA", "TSLA", "META", "AMZN"]
MIN_VALUE = 100000  # Alert on purchases over $100K

def check_insider_buys():
    alerts = []

    for ticker in WATCHLIST:
        resp = requests.get(f"{API_URL}/sec/insider-trades/company/{ticker}", params={
            "days": 1,
            "limit": 20
        })
        data = resp.json()

        for filing in data.get("filings", []):
            # Get full details to check transaction value
            detail = requests.get(f"{API_URL}{filing['detailUrl']}")
            info = detail.json()

            for tx in info.get("transactions", []):
                if tx["code"] == "P":  # Open market purchase
                    value = (tx.get("shares", 0) or 0) * (tx.get("pricePerShare", 0) or 0)
                    if value >= MIN_VALUE:
                        alerts.append({
                            "ticker": ticker,
                            "insider": info["owner"]["name"],
                            "shares": tx["shares"],
                            "value": value,
                            "date": tx["date"]
                        })

    return alerts

# Run it
buys = check_insider_buys()
for buy in buys:
    print(f"🚨 {buy['ticker']}: {buy['insider']} bought {buy['shares']} shares (${buy['value']:,.0f})")

JavaScript Version (Node.js)

const API_URL = "https://sec-edgar-insider-alerts-production.up.railway.app";

// Fetch recent insider trades
async function getInsiderTrades(ticker, days = 7) {
  const res = await fetch(
    `${API_URL}/sec/insider-trades/company/${ticker}?days=${days}&limit=50`
  );
  const data = await res.json();

  return data.filings.map(f => ({
    date: f.filedDate,
    insider: f.insider.name,
    company: f.company.name,
    detailUrl: f.detailUrl
  }));
}

// Get transaction details
async function getFilingDetail(accession) {
  const res = await fetch(`${API_URL}/sec/insider-trades/filing/${accession}`);
  return res.json();
}

// Example: Find all purchases in NVDA this month
const trades = await getInsiderTrades("NVDA", 30);
console.log(`Found ${trades.length} NVDA insider filings`);

for (const trade of trades.slice(0, 5)) {
  console.log(`${trade.date} — ${trade.insider}`);
}

Use Cases

  • Trading signals: Cluster analysis on insider buys (3+ insiders buying within a week is historically bullish)
  • Compliance dashboards: Monitor executive trading windows and blackout periods
  • Financial research: Backtest insider sentiment vs. stock performance
  • Slack/Discord bots: Push alerts when specific insiders trade
  • Portfolio screening: Filter your holdings by recent insider activity

Data Source & Reliability

The API pulls directly from SEC EDGAR’s EFTS API and ATOM feed — official government data, not scraped from third-party sites. Polling happens every 5 minutes during market hours, with fallback to the ATOM feed if EFTS is down.

Covers all Form 4 filers across NYSE, NASDAQ, and OTC markets. No rate limiting on the direct endpoint.

Try It

The API is live and free to use:

No API key needed for the direct Railway endpoint. Just start making requests.

“Why I Stopped Trusting Backtests and Built a Live Paper Trading Tournament Instead”

Backtesting feels productive. You write a strategy, run it against 5 years of historical data, watch the equity curve trend up and to the right, and start daydreaming about returns.

Then you paper trade it. And it falls apart within the first two weeks.

I’ve been through this cycle enough times to know that backtesting and live trading are fundamentally different problems. Backtesting is optimization. Live trading is survival.

What Backtesting Actually Tests

Backtesting tests whether your strategy would have worked on data that you implicitly shaped your strategy around. Even with walk-forward validation and out-of-sample testing, there’s survivor bias baked in — you chose the instruments, the time period, the features. The strategy is always somewhat aware of the future it’s supposedly predicting.

The four biases I kept running into (and eventually fixed in TradeSight):

  1. Look-ahead bias — using daily close prices as if they’re available intraday
  2. Survivorship bias — only testing on stocks that still exist
  3. Overfitting — RSI + MACD + Bollinger + custom filter = it works on exactly this data
  4. Slippage blindness — assuming fills at the exact signal price

These aren’t edge cases. They’re the default state of most backtesting setups.

The Tournament Idea

The shift I made: instead of running one strategy and judging it on historical performance, I run multiple strategies simultaneously against live paper trading data and let them compete.

The setup is simple. Each strategy gets the same starting capital. They all receive the same market data in real-time via Alpaca’s paper trading API. At the end of a session (I run overnight — markets are less noisy for my RSI-based signals), I score each strategy on:

  • Net return
  • Max drawdown
  • Win rate
  • Sharpe ratio
  • Number of trades (penalizes churning)

The top performers roll forward. The losers get replaced or adjusted. It’s evolutionary selection on a small scale.

Why This Works Better

A few things surprised me when I switched to this approach:

Failure is fast. A strategy that’s broken in live conditions fails within days, not months. With backtesting, I’d build elaborate validation pipelines to catch flaws. With tournaments, the market does it for you.

Signal quality becomes obvious. When a strategy is profitable in backtesting but losing in live trading, it’s almost always overfitting or look-ahead bias. The gap between backtest and live performance is diagnostic data.

You stop over-engineering. When you’re running 4-6 strategies at once with real-time data, simple beats complex almost every time. RSI crossover with clean entry/exit rules usually outperforms my “improved” version with 8 extra filters.

The Implementation

The core of TradeSight is a signal loop that runs every 30 minutes during market hours and overnight for swing setups:

for strategy in active_strategies:
    signals = strategy.generate_signals(market_data)
    for signal in signals:
        if passes_risk_guard(signal, portfolio_state):
            execute_paper_trade(signal)

    strategy.score = calculate_score(strategy.trade_history)

# Tournament: keep top performers
ranked = sorted(active_strategies, key=lambda s: s.score, reverse=True)
active_strategies = ranked[:keep_top_n]

The risk guard is important — it enforces position sizing limits and a correlation guard that prevents the portfolio from doubling up on correlated instruments (which backtests almost never catch).

What I’ve Learned From Running Tournaments

After running this for a few months:

  • RSI with sector-adjusted thresholds consistently outperforms vanilla RSI
  • Mean reversion beats momentum on overnight holding periods for the instruments I trade
  • Drawdown limits matter more than return targets — strategies that survive drawdowns compound better over time
  • The correlation guard saves you regularly — tech sector moves together and naive strategies pile into correlated positions

The most useful output isn’t “this strategy made 3.2% this week.” It’s “this strategy’s drawdown profile looks unstable — here’s why.”

The Actual Code

TradeSight is open source: github.com/rmbell09-lang/tradesight

It runs on Python 3.9+, connects to Alpaca for paper trading data, and stores tournament results in SQLite. Setup takes about 10 minutes if you have an Alpaca paper account (free to create).

If you’re tired of backtesting giving you false confidence, this approach is worth trying. It won’t make bad strategies good — but it’ll tell you which ones are actually bad a lot faster.

Built a similar system? Running paper trading tournaments against something other than RSI? I’d like to hear what’s working.

How I Built a Memory-Safe Steganography Engine in Rust to Protect Data from AI Scrapers

How I Built a Memory-Safe Steganography Engine in Rust to Protect Data from AI Scrapers

As AI models scale, data provenance is becoming a massive engineering challenge. Automated web scrapers are vacuuming up datasets without any regard for creator licenses or intellectual property.

I wanted to build a mathematical solution to this problem, so I architected Sigil: a zero-knowledge cryptographic vault that embeds verifiable, HMAC-SHA256 signed ownership IDs directly into the pixels of an image.

While the desktop vault (built with Tauri, Svelte, and an offline SQLite daemon) is strictly closed-source to protect the cryptographic keys, I realized that “Security by Obscurity” isn’t enough. If AI companies don’t know how to read the hidden IDs, they will just scrape the images anyway.

So, I open-sourced the extraction layer. Here is a deep dive into how I used Rust to build a memory-safe Least Significant Bit (LSB) steganography reader.

The Concept: LSB Steganography

Every pixel in a standard image is made of Red, Green, and Blue channels. Each channel is represented by a byte (8 bits), with values ranging from 0 to 255.

If you change the Least Significant Bit (the absolute last 1 or 0 in that byte), the color change is invisible to the human eye. But mathematically, you can use those hidden bits to store a secret payload—like a 32-byte cryptographic ID.

The Rust Implementation

To make this blazing fast and completely safe from memory leaks, I used Rust’s image crate to parse the pixels. Here is the exact open-source reference implementation for extracting the payload:

use image::GenericImageView;

/// Extracts a hidden Sigil Cryptographic ID from an image's LSB layer.
pub fn verify_steganography(path: &str, expected_id_len: usize) -> Result<String, String> {
    let img = image::open(path).map_err(|e| e.to_string())?.to_rgba8();
    let mut bits = Vec::with_capacity(expected_id_len * 8);

    // 1. Extract the Least Significant Bits
    for pixel in img.pixels() {
        for channel in 0..3 { // Iterate over R, G, B
            if bits.len() < expected_id_len * 8 {
                // The bitwise AND operator isolates the final bit
                bits.push(pixel[channel] & 1);
            }
        }
    }

    // 2. Reconstruct the bytes
    let mut extracted_bytes = Vec::new();
    for chunk in bits.chunks(8) {
        if chunk.len() == 8 {
            let mut byte = 0u8;
            for (i, &bit) in chunk.iter().enumerate() {
                // Shift the bit back into its correct position
                byte |= bit << (7 - i);
            }
            extracted_bytes.push(byte);
        }
    }

    // 3. Return the Hex String
    Ok(hex::encode(extracted_bytes))
}

Breaking Down the Bitwise Math

The magic happens in two specific lines of code:

  • pixel[channel] & 1: This is a bitwise AND operation. By comparing the pixel’s byte against 00000001, we wipe out the first 7 bits and isolate only the final bit. If the pixel is even, it returns 0. If it is odd, it returns 1. We push this bit into our Vector.
  • byte |= bit << (7 - i): Once we have 8 hidden bits, we need to stitch them back into a single byte. We use the left-shift operator (<<) to push the bit into the correct slot (from position 7 down to 0), and the bitwise OR operator (|=) to combine them into the final u8.

The Open Standard

By publishing this extraction logic, AI procurement teams can now integrate this exact function into their scraping pipelines. If their crawler detects a payload, they know the asset is cryptographically locked and requires an API clearance.

You can check out the full open-source extraction standard on GitHub, or visit the lightning-fast Astro-powered documentation site here:
https://nishal21.github.io/Sigil-extractor/

I’d love to hear your thoughts on the Rust implementation or LSB steganography in general!

https://github.com/nishal21/Sigil-extractor

Docling CLI to parse PDFs and export it to multiple formats

What is Docling ???

Docling is an open source document processing library that converts various document formats into structured outputs.
Docling plays an important part in the RAG pipeline.

I’ll be taking you through the process of parsing PDFs into structured formats.

Step 1: Set up

  • Create the project structure in your terminal;
mkdir docling_cli
cd docling_cli
  • Create your virtual environment and activate it.
    Fedora


Windows

Step 2: Installing docling

pip install docling
docling --version

Fedora

Windows

Check the docling’s version

Step 3: Creating input and outputs folders

  • create a folder called data where you will stored your desired pdfs.
  • create a new folder and name it outputs
    then inside the folders create new folders called; markdown outputs, html outputs and json outputs.

Step 4: Changing the pdfs into html format

docling --to html *.pdf --output ~Documents/docling_cli/outputs/html_outputs

Step 5: Changing the pdfs into other formats

1. Markdown

2. Json

3. Plain text

4. yaml

5. html_split_page

6. DOCtags

7. vtt

Step 6: Analyzing the result findings.

I used three types of pdfss;
one with tables, the other with text and images and the other had tables and paragraphs. Here are my key findings;

1. Pdf with tables

  • In HTML, the rows and columns came out better than they were in the original pdf.
  • Markdown outputs were good too as it wrote the tables in markdown format without losing anything.
  • JSON was broke everything down into nested objects
  • Plain text was good too but not as compared to markdown.

2. Pdf with text and images

  • HTML lost the color of the images.

3. Pdf with tables and paragraphs

  • Paragraphs in all formats came out nicely as texts.

How to Detect When a Competitor Launches a New Feature Before Your Sales Team Notices

Your sales rep is halfway through a demo when the prospect asks: “Can your product do X? We heard [a competitor] just added that last month.”

You had no idea.

It’s one of the most deflating moments in B2B sales — and it’s almost entirely preventable with a two-hour automation setup. This article shows you how to build it.

The Problem: Competitor Product Intelligence Has a Structural Gap

Most B2B SaaS teams have no real-time monitoring for competitor product changes. They rely on:

  • Sales reps to surface insights from customer conversations (reactive, weeks late)
  • Marketing team members to manually check competitor websites (inconsistent, unmaintained)
  • Paid competitive intelligence tools that often lag by days and cost $200–$500/month
  • Social media monitoring that misses silent changelog updates entirely

The result: your team learns about competitor feature launches from prospects, not from internal intelligence. By then, the deal may already be tilted.

The root cause isn’t effort — it’s structure. Competitor product pages, changelogs, and blogs change without notifying you. There’s no RSS feed for competitor product decisions. No Slack integration that fires when a rival ships a major update. You have to pull that data — and almost nobody has a system to do it automatically.

Why Manual Monitoring Breaks Down

Let’s be specific about where the gap is:

Changelog pages. Most SaaS companies maintain a changelog at /changelog or /releases. These pages are updated on irregular schedules — sometimes weekly, sometimes after a silent 6-week freeze followed by a burst of three major features. Nobody on your team checks this every week.

Product/feature pages. Feature landing pages quietly gain new sections, new capability descriptions, new comparison tables. A “Basic plan” quietly becomes “Starter + AI Assistant.” You won’t notice unless you’re watching.

Blog posts. Feature launch announcements typically go out as blog posts first — before the feature even fully ships. A blog post titled “Introducing [Feature Category]” is often your earliest warning signal, and it’s completely searchable.

SERP visibility. When a competitor is about to ship, they often start appearing in new search queries. Monitoring their brand name alongside product terms (“new feature,” “launch,” “update”) gives you early signal before the blog post even ranks.

None of this is hidden. All of it is publicly accessible. The problem is that pulling and comparing it at scale requires infrastructure most teams don’t have.

The Defense Pattern: Structured Change Detection

Here’s the architecture that works:

  1. Extract — pull the full text content of competitor changelog pages, product pages, and blog post listings weekly
  2. Diff — compare today’s content to last week’s stored snapshot
  3. Alert — when new sections or new posts appear, fire a Slack message or email to your product/sales lead

The key insight is that you don’t need to read competitor pages — you need to detect changes in them, and then have a human read only the changed sections. That’s a five-minute weekly review instead of a 30-minute manual audit.

Implementation: Apify + JavaScript

Apify provides two actors that handle this well on a free tier:

  • apify/website-content-crawler — extracts clean text from any webpage, including JS-rendered content
  • lanky_quantifier/google-serp-scraper — runs Google searches and returns structured results

Here’s a working Node.js implementation:

import { ApifyClient } from 'apify-client';
import * as fs from 'fs';
import * as crypto from 'crypto';

const client = new ApifyClient({ token: process.env.APIFY_API_TOKEN });

// Pages to monitor — add competitor changelogs, feature pages, blog indexes
const PAGES_TO_MONITOR = [
  'https://competitor-a.com/changelog',
  'https://competitor-a.com/features',
  'https://competitor-b.com/whats-new',
  'https://competitor-b.com/blog',
];

// SERP queries to monitor for launch signals
const SERP_QUERIES = [
  '"competitor-a" new feature 2024',
  '"competitor-a" launch announcement',
  '"competitor-b" product update',
];

async function extractPageContent(url) {
  const run = await client.actor('apify/website-content-crawler').call({
    startUrls: [{ url }],
    maxCrawlPages: 1,
    outputFormats: ['text'],
  });

  const { items } = await client.dataset(run.defaultDatasetId).listItems();
  return items[0]?.text || '';
}

async function checkSerpForLaunchSignals(query) {
  const run = await client.actor('lanky_quantifier/google-serp-scraper').call({
    queries: query,
    maxPagesPerQuery: 1,
    resultsPerPage: 10,
  });

  const { items } = await client.dataset(run.defaultDatasetId).listItems();
  return items.filter(r => r.position <= 5).map(r => ({
    title: r.title,
    url: r.url,
    snippet: r.snippet,
  }));
}

function hashContent(text) {
  return crypto.createHash('sha256').update(text).digest('hex');
}

function loadSnapshot() {
  if (fs.existsSync('./competitor-snapshot.json')) {
    return JSON.parse(fs.readFileSync('./competitor-snapshot.json', 'utf-8'));
  }
  return {};
}

function saveSnapshot(snapshot) {
  fs.writeFileSync('./competitor-snapshot.json', JSON.stringify(snapshot, null, 2));
}

async function runMonitor() {
  const snapshot = loadSnapshot();
  const changes = [];
  const newSnapshot = {};

  // Check competitor pages for content changes
  for (const url of PAGES_TO_MONITOR) {
    console.log(`Checking: ${url}`);
    const content = await extractPageContent(url);
    const hash = hashContent(content);
    newSnapshot[url] = { hash, checkedAt: new Date().toISOString() };

    if (snapshot[url] && snapshot[url].hash !== hash) {
      changes.push({
        type: 'PAGE_CHANGED',
        url,
        message: `Content changed on ${url}`,
      });
    }
  }

  // Check SERP for launch signals
  for (const query of SERP_QUERIES) {
    const results = await checkSerpForLaunchSignals(query);
    const topResult = results[0];
    if (topResult) {
      const key = `serp:${query}`;
      const topUrl = topResult.url;
      newSnapshot[key] = { topUrl, checkedAt: new Date().toISOString() };

      if (snapshot[key] && snapshot[key].topUrl !== topUrl) {
        changes.push({
          type: 'SERP_CHANGED',
          query,
          newTop: topResult,
          message: `New top result for query: "${query}"`,
        });
      }
    }
  }

  saveSnapshot(newSnapshot);

  if (changes.length > 0) {
    console.log('n🚨 COMPETITOR CHANGES DETECTED:');
    changes.forEach(c => console.log(`  - ${c.message}`));
    await sendAlert(changes);
  } else {
    console.log('n✅ No changes detected this week.');
  }
}

async function sendAlert(changes) {
  const webhookUrl = process.env.SLACK_WEBHOOK_URL;
  if (!webhookUrl) return;

  const text = changes.map(c => `• *${c.type}*: ${c.message}`).join('n');
  await fetch(webhookUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `🔍 *Competitor Intelligence Alert*nn${text}nnReview manually and route to product/sales.`,
    }),
  });
}

runMonitor().catch(console.error);

Scheduling the Monitor

Run this weekly using Apify’s built-in scheduler or a cron job:

# cron entry — runs every Monday at 8am
0 8 * * 1 cd /your/project && node monitor.js >> /var/log/competitor-monitor.log 2>&1

Or schedule it directly in the Apify console as a recurring Task — no server required.

What to Monitor

Start with these page types for maximum signal density:

Page Type Why It Matters
/changelog or /releases Direct feature shipping signal
/features or /product Quiet capability expansions
/blog (index page) First announcement vehicle
SERP: "brand" + "new" + "feature" Pre-launch SEO positioning
SERP: "brand" + "vs" + "your-brand" Comparison page updates

Three to five competitors, four to six pages each — that’s 15–30 monitored URLs. Apify’s free tier handles this easily.

Cost

Apify free tier: $5/month credit, renewing monthly. A weekly run monitoring 20–30 URLs via website-content-crawler costs roughly $0.10–$0.30 per run. You’ll stay within free tier limits.

The alternative cost: One lost deal to a competitor feature you didn’t know existed can be worth $5,000–$50,000 in ARR. The monitoring pays for itself the first time it fires.

What You Actually Get

The system fires when something changes. A human then spends five minutes reading the changed section and deciding whether to act. That’s the design — automated detection, human judgment on significance.

No more learning about competitor launches from prospects. No more scrambling to prepare objection handling the day before a major demo. Your sales team gets early warning. Your product team gets signal on what your market values enough to ship.

The gap between “reactive” and “proactive” competitive intelligence is two hours of setup time and $0 in monthly cost.

This tutorial uses Apify actors available at apify.com. The website-content-crawler and google-serp-scraper actors are free-tier compatible. No API key required for the Apify free plan — create an account and you’re ready to run.

I built an open-source system to track how engineers actually adapt to AI

The problem

Everyone has opinions about what engineers should do in response to AI. Almost no one has data about what they actually do.

I wanted data.

What I built

HumanExodus is a longitudinal observation system. It captures how engineers respond to AI pressure at the moment it’s happening, then follows up 30 days later to find out what actually happened.

The gap between intention and reality is the dataset.

How it works

  1. Engineer enters role, experience, and tech stack
  2. Rule-based engine estimates AI exposure level (HIGH / MEDIUM / LOW)
  3. Claude API generates personalised adjacent moves based on their profile
  4. Engineer selects their intended next step
  5. Session saved to Supabase
  6. 30 days later: automated email via Resend asks what actually happened
  7. Outcome saved as a follow-up record

What we’re seeing so far

With just 11 sessions, a pattern is already emerging:

  • HIGH exposure engineers: high uncertainty (40% “not sure”)
  • MEDIUM exposure engineers: mostly staying put (80% “stay same”)
  • LOW exposure engineers: 100% staying same

This is early and small. But it’s real behavioural data, not survey responses.

The tech stack

  • Frontend: single HTML file, no build step, no framework
  • Database: Supabase (Postgres)
  • AI: Claude API for move generation
  • Email: Resend
  • Hosting: GitHub Pages

The moat

The code is simple. The data is not.

If this works, HumanExodus becomes the largest structured dataset of how engineers adapt to AI — role by role, stack by stack, over time.

What’s next

  • v0.5: Predictive layer — “people like you tend to move into X within 30-60 days”
  • But only after real follow-up data exists

Try it / contribute

Building a Desktop Control Center for OpenClaw with Tauri and Rust

Analytics dashboard

I’ve been working with OpenClaw for managing AI agents, and I kept running into the same problems: juggling multiple instances for different environments, manually editing openclaw.json, and lacking visibility into token usage and costs.

So I built Pond — a desktop app that sits on top of OpenClaw and solves these issues.

Screenshots (from the repo README)

Analytics dashboard

Role management

Team tasks

Agent chat

What it does

Pond is a control center that lets you:

  • Manage multiple OpenClaw instances on one machine (like dev/test/prod environments)
  • Orchestrate team collaboration with a built-in task state machine (open → claimed → done/failed)
  • Monitor in real-time — tokens, costs, sessions, system resources
  • Edit configs visually instead of wrestling with JSON files

Each instance runs independently with its own Gateway process, port, config, and team data.

Technical stack

I chose Tauri 2 instead of Electron because I wanted native performance and smaller binaries. The backend is Rust:

  • tokio::process for managing Gateway subprocesses
  • fs4 for cross-platform file locks (team data concurrency)
  • Native WebSocket plugin for bidirectional streaming with OpenClaw

The frontend is React 19 + TypeScript + Zustand for state, with Radix UI components and TailwindCSS.

The team collaboration layer

One unique feature is the collaboration workflow. You define roles in agents.list (a Leader and executors), and Pond manages a task lifecycle:

  1. Leader creates tasks (state: open)
  2. Executors claim tasks (state: claimed)
  3. Executors mark them done or failed with reasons
  4. Real-time notifications pushed via WebSocket to relevant roles

There’s a built-in pond-team skill that orchestrates this entire workflow without manual coordination.

What’s missing

It’s still early (v1.0.4), and there are gaps:

  • No cloud sync for configs yet
  • No mobile clients (only desktop)
  • Skill marketplace is planned but not built

But the core features work, and it’s fully open source.

Try it

Installers for macOS (Intel + Apple Silicon), Windows, and Linux are on the GitHub releases page:

👉 https://github.com/tageecc/pond

If you’re building with OpenClaw, I’d appreciate feedback — especially around what metrics or workflows you need most.