Pixel

Pixel Updates & Events

Server Updates & Events

Announcements

DEV Community

I Built a 100% Offline Resume Builder So I Wouldn't Have to Share My Phone Number & Email

Tired of spam calls and paywalled PDFs, I created PrivyResume—a local-first resume workbench that keeps your personal data strictly in your browser. Whenever you try to update your resume online, almost every website follows the exact same playbook: Let you spend 30 minutes typing out your entire career history. Force you to create an account with your email and phone number. Hit you with a surprise paywall right when you click "Download PDF." Sell or leak your contact details—leading to endless spam calls and shady recruiter messages. I got tired of handing over my personal phone number and email address to random databases just to generate a clean PDF. So, I built PrivyResume for my own use. What is PrivyResume? PrivyResume is a 100% client-side, offline-capable resume workbench. There are no user accounts, no backend databases, and zero tracking scripts. Here is how it works: Full Privacy First (Zero Servers): Your phone number, email address, and work history stay strictly inside your browser's localStorage. Nothing ever leaves your device or touches a server. Multi-Profile Support: Create and switch between multiple resume versions locally (for example, one tailored for Frontend roles and another for Full-Stack) right inside the browser. 1-Click Share Link: Need to send a live version quickly? Generate a shareable link with a single click. Export & Import via JSON: Because your data lives locally in your browser, you can download your resume as a raw .json file anytime to back it up or move it to another device. Free PDF Export & Print: Generate clean, print-ready PDFs with zero watermarks or paywalls. Built to Scratch My Own Itch I originally built this purely as a personal tool so I could keep my master resume backed up as a simple JSON file and export clean PDFs without worrying about data harvesting. If you are currently job hunting—or just want a fast, private way to maintain your resume without creating yet another account—give it a try here: Try PrivyResume I would love to hear your thoughts, feedback, or feature ideas in the comments!

Powered by Readybot.io

**DEV Community** ## I Built a 100% Offline Resume Builder …

DEV Community

How I built a "chess-engine" style AI for a 6-dice strategy game natively on my Pixel 9 Pro

I just moved a solo project out of closed testing into production, and the engineering constraints almost broke me. The app is Maxi Yatzy Master, a digital version of the Scandinavian 6-dice variant of Yahtzee. The core game is completely free to play, but building a worthy AI opponent for it required solving a massive compute bottleneck. The Math Problem Standard 5-dice Yahtzee is largely luck-based, but the 6-dice variant allows players to bank unused rolls for later turns. This single mechanic creates incredibly complex diverging paths across different game states. Most mobile dice games use rigid logic trees or simply cheat the dice to simulate difficulty. Calculating the true optimal move required serious compute. To solve this, I built the game logic and probability statistics using a codebase architecture similar to advanced chess simulators. The AI is game-aware; it actively reads the opponent's scorecard and dynamically pivots its risk tolerance to maximize its actual win probability, rather than just chasing a raw high score. The Mobile-First Toolchain To make things harder on myself, I engineered the entire project natively on my phone. Local Environment: Built natively in the Claude Code app on Android. Multiplayer: Bypassed heavy matchmaking servers to build a lightweight, serverless Peer-to-Peer architecture using Firebase rules for online play. Deployment: GitHub, Cloudflare Pages, and Netlify. Feedback Engine: Built an offline analysis tool that flags mathematically suboptimal choices, tracks play themes, and allows players to jump back to replay specific turns. The Result It’s exactly the app I hoped it would be when I set out to build it, and it went through rigorous closed testing with a dozen real players to tune the mechanics. If any of you want to test out the AI's compute performance on your own device, or stress-test the Firebase P2P syncing, the app is live here: https://play.google.com/store/apps/details?id=com.maxiyatzy.trainer I’d love to hear your thoughts on the architecture, and I'm happy to answer any questions about the mobile-native workflow!

Powered by Readybot.io

**DEV Community** ## How I built a "chess-engine" style AI …

DEV Community

SortMail Latest 🚀

Been quiet, but the building hasn't stopped! SortMail updates: ✅ Custom domain ✅ Professional support email ✅ Privacy Policy & Terms, FAQ & Support pages Now preparing for Google verification and public launch. 🌐 sortmail.online BuildInPublic

Powered by Readybot.io

**DEV Community** ## SortMail Latest 🚀 Been quiet, but the …

DEV Community

Windows GUI check — published sentinel-ir-mcp 0.5.9

Verdict Source (GitLab, Apache-2.0): https://gitlab.com/jakubrojicek11/sentinel-ir npm: https://www.npmjs.com/package/sentinel-ir Playground (runs in your browser): https://sentinel-ir-playground-dc30c8.gitlab.io/ Desktop startup is verified; desktop-originated tool calls remain untested. Normally installed VS Code and its Cline extension both started the published server without signing in. Actual analysis calls passed through the MCP Inspector browser fallback, not a desktop client. VS Code also reproduced the reported Error/empty-output symptom, but on its session-bound server entry. The separate locally configured / Global in Code entry started successfully with the same configuration. Evidence points to VS Code's session/policy surface, not a broken Sentinel executable. The exact underlying VS Code policy requirement is not established. Environment and configuration Windows Server 2022; VS Code 1.146.0, official user installer, not portable. Onboarding bypassed with Continue without Signing In. No Microsoft/GitHub/Cline login. Primary runtime Node 22.23.5, secondary Node 24.22.2; spawned server ExecutablePath checked using Win32_Process, not inferred from shell PATH. Project: C:\t\Projekt Žluťoučký kůň\app; files src\index.js, src\util.js, src\main.rs, tool.py. Setup invoked as npx -y [email protected] setup --client vscode. It printed a command; it did not write configuration. Registered using the printed code --add-mcp form; resulting user configuration: %APPDATA%\Code\User\mcp.json. Actual running package: %LOCALAPPDATA%\npm-cache_npx\3ec5bc0582433d0b\node_modules\sentinel-ir-mcp\package.json → 0.5.9. Both Node versions used this published npm cache installation. VS Code's generated entry: {"type":"stdio","command":"npx","args":["-y","[email protected]","C:\t\Projekt Žluťoučký kůň\app"]}
For Cline and Inspector, copied the Windows JSON-client form confirmed by setup --client claude-desktop --project "" --config --dry-run: {"command":"cmd","args":["/c","npx","-y","[email protected]","C:\t\Projekt Žluťoučký kůň\app"]}
Verified results Client / surface Started? Tools / calls VS Code 1.146.0, local user entry, Node 22 Yes Running; Discovered 3 tools; real Sentinel Node 22 process. Initial restricted workspace worked; trusting it and restarting also worked. Cline 4.1.26, Node 22, generated cmd /c npx Yes Green connected indicator; schemas for analyze_code, analyze_file, analyze_directory. Calls untested: no configured working model. Selected local Ollama provider to finish onboarding without an account; no model service was installed. MCP Inspector 2.15.0 browser fallback, Node 22, same cmd /c npx Yes All three tools listed. Both required real calls passed; results below. VS Code 1.146.0, session-bound entry after relaunch, Node 24 environment No Error, empty MCP output; renderer: Could not list installed MCP servers. (refused: policy-context-unavailable). No additional Sentinel process from this attempt. Cline's separately running Node 24 server was not mistaken for this entry. VS Code 1.146.0, local / Global in Code entry, Node 24 Yes Same generated npx configuration; Running + Discovered 3 tools; separate actual Node 24 server process. VS Code local entry, Node 24, npx.cmd variant Yes Changed only command from npx to npx.cmd; Running + Discovered 3 tools. Original generated configuration restored afterward. Inspector calls: entered absolute Czech/spaced paths through visible tool forms and clicked Execute Tool. analyze_file on ...\src\index.js: OK, 75 ms in result pane; analysisStatus: complete, language: javascript, risk: medium, filesystem and network recognized (fs.readFileSync, https.get). analyze_directory on project root: OK, 39 ms; 4 analyzed, 4 complete, 0 incomplete, no timeout/truncation; Rust/Python critical (process spawning), index.js medium, util.js low. Protocol monitoring panel separately displayed approximately 73 ms / 34 ms for those calls. No C...

Powered by Readybot.io

**DEV Community** ## Windows GUI check — published sentinel-ir-mcp 0.5.9 …

DEV Community

I built a standup orchestrator that runs inside your AI agent (and doesn't spy on anyone)

When my team started writing standups with AI, three problems showed up: Loose updates. Everyone wrote in a different format, in a different place. No view of the team. Whoever read them had to stitch messages together. Nobody orchestrates. Nobody nudges who hasn't posted, and blockers sit until someone notices. Standcast fixes this by keeping the writing where people already work, in their agent, and handling the rest. The flow You run /daily in Claude Code (or use the MCP server in Cursor, Codex, Claude Desktop or VS Code, or the CLI). The daily opens with what you planned in the previous one and whether each item moved. Standcast drafts "yesterday" from real activity: pull requests opened, merged and reviewed, commits, Linear cards that moved and your Claude Code sessions, grouped by project in up to 5 lines. You edit it, write "today", blockers and support requests (what you need from someone without being stuck: a review, a decision, a pairing session) and confirm. The MCP submit tool requires confirmed: true, so the agent can't send it on its own. At the team's digest_at, a worker posts one digest per team to Slack or Google Chat: 🚧 blockers first, then 🤝 support requests. In the web app, Today shows the team's day: open blockers with their age, recurrence and a "probably resolved" hint when the PR already merged, support requests, today's dailies and what shipped yesterday. History keeps past digests, searches confirmed dailies and exports shipped work as Markdown or CSV. The parts that took real work AI sessions without leaking anything. The CLI reads the Claude Code session files on your machine, redacts secrets and hands only a summary to the agent. No session data reaches the API: only the daily text you confirm. Configuration as code. A standcast.yml in the organization's .github repository, which you can also edit through a form in the web app. Every push that changes the file gets a check run with the error on the right line, and an invalid push never breaks anything: the last valid version keeps working. Routing blockers. A blocker that mentions a pull request goes by DM to its reviewers, then CODEOWNERS, then the team's managers. On Linear, to the owner of the blocking card. If it is still open after N days, the team gets one alert. Sending exactly once. Every message goes through a database claim with a fencing token before it reaches the chat. A job retry never sends twice. Time zones and workdays. remind_at and digest_at are local times of the team; Monday covers the weekend. The rule I won't bend No per-person metrics, no rankings, no history of who didn't post. "Who hasn't posted yet" exists only for today, in the web app. Standcast also suggests blockers (a PR waiting for review, CI red for a day, a card stuck in progress), but those suggestions belong only to the person: they never show up in the digest, the API, webhooks or for the manager. A CI test makes sure of it. Stack Bun, strict TypeScript, Hono, Postgres with Drizzle, pg-boss for queues, a GitHub App, Slack, Google Chat and Linear. The CLI is a single binary with the MCP server built in. The web app is React 19 with TanStack Router and ker. MIT. Open beta It's in free beta and I'm looking for teams of 3 to 15 people on GChat. If you want to try it, leave a comment or open an issue and I'll help you set it up. Site: https://standcast.app Repository: https://github.com/ImFelipeOliveira/standcast Docs: https://docs.standcast.app Feedback is welcome.

Powered by Readybot.io

**DEV Community** ## I built a standup orchestrator that runs …

DEV Community

Estimating Proxy Bandwidth for Public Data Collection in Kazakhstan with Python

Collecting public data responsibly is not only a question of writing faster code. Before increasing concurrency, you need to estimate the traffic volume, the available time window, retry overhead, and the throughput your workload can actually sustain. This guide builds a small Python model for that calculation. It is intended only for requests you are authorized to make. Respect applicable law, the target website's terms, robots directives, and rate limits. 1. Start with the theoretical minimum For decimal terabytes, the average throughput required to transfer a given volume is: required_Mbps = traffic_TB × 8,000,000 ÷ seconds
seconds = days × 86,400
For example, moving 100 TB evenly over 30 days requires: 100 × 8,000,000 ÷ 2,592,000 = 308.6 Mbps
That is only the theoretical average. It assumes continuous transfer, no retries, no pauses, and no protocol overhead. 2. Add utilization and retry overhead A practical estimate should reserve capacity for variable latency, connection setup, retries, and quiet periods. The function below makes those assumptions explicit. from dataclasses import dataclass

SECONDS_PER_DAY = 86_400
BITS_PER_TERABYTE = 8_000_000 # Mbps-seconds in one decimal TB

@dataclass(frozen=True)
class BandwidthEstimate:
theoretical_mbps: float
planned_mbps: float

def estimate_bandwidth(
traffic_tb: float,
days: int,
utilization: float = 0.70,
retry_fraction: float = 0.10,
) -> BandwidthEstimate:
if traffic_tb <= 0:
raise ValueError("traffic_tb must be positive")
if days <= 0:
raise ValueError("days must be positive")
if not 0 < utilization <= 1:
raise ValueError("utilization must be between 0 and 1")
if retry_fraction < 0:
raise ValueError("retry_fraction cannot be negative")

seconds = days * SECONDS_PER_DAY
theoretical = traffic_tb * BITS_PER_TERABYTE / seconds
planned = theoretical * (1 + retry_fraction) / utilization

return BandwidthEstimate(
    theoretical_mbps=round(theoretical, 1),
    planned_mbps=round(planned, 1),
)

estimate = estimate_bandwidth(traffic_tb=100, days=30)
print(estimate)
With 70% utilization and 10% retry overhead, the result is approximately: BandwidthEstimate(theoretical_mbps=308.6, planned_mbps=485.0)
The gap between 308.6 Mbps and 485.0 Mbps is not wasted bandwidth. It is operational headroom for a workload that does not run perfectly every second of the month. 3. Compare the result with standard throughput levels The following table shows the maximum theoretical transfer volume when a connection runs continuously at the listed rate. Throughput 1 day 7 days 30 days 100 Mbps 1.08 TB 7.56 TB 32.4 TB 200 Mbps 2.16 TB 15.12 TB 64.8 TB 400 Mbps 4.32 TB 30.24 TB 129.6 TB 600 Mbps 6.48 TB 45.36 TB 194.4 TB 800 Mbps 8.64 TB 60.48 TB 259.2 TB 1 Gbps 10.8 TB 75.6 TB 324 TB For the 100 TB example, 400 Mbps covers the theoretical average but leaves limited room for retries and idle periods. A 600 Mbps tier provides more practical headroom under the assumptions above. 4. Measure latency instead of guessing A bandwidth plan alone does not guarantee application throughput. Measure representative endpoints before selecting concurrency. import statistics
import time
import requests

def measure_latency(url: str, samples: int = 20) -> dict[str, float]:
durations = []

for _ in range(samples):
    started = time.perf_counter()
    response = requests.get(url, timeout=15)
    response.raise_for_status()
    durations.append((time.perf_counter() - started) * 1000)
    time.sleep(0.5)

ordered = sorted(durations)
p95_index = max(0, int(len(ordered) * 0.95) - 1)

return {
    "p50_ms": round(statistics.median(ordered), 1),
    "p95_ms": round(ordered[p95_index], 1),
    "mean_ms": round(statistics.mean(ordered), 1),
}

Use an endpoint you control or have permission to test. Do not benchmark a third-party website aggressively. P95 latency ...

Powered by Readybot.io

**DEV Community** ## Estimating Proxy Bandwidth for Public Data Collection …

DEV Community

SQL COUNT After a LEFT JOIN: Why Zero Orders Can Become One

A customer with no orders can accidentally receive an order count of one. The SQL runs successfully; the error is in what is being counted. A minimal PostgreSQL example WITH customers(customer_id) AS (
VALUES (1), (2)
), orders(order_id, customer_id, status) AS (
VALUES (101, 1, 'paid'),
(102, 1, 'pending')
)
SELECT c.customer_id,
COUNT() AS joined_rows,
COUNT(o.order_id) AS order_count,
COUNT(o.order_id) FILTER (WHERE o.status = 'paid')
AS paid_order_count
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.customer_id
GROUP BY c.customer_id
ORDER BY c.customer_id;
Expected results by inspection: customer_id joined_rows order_count paid_order_count 1 2 2 1 2 1 0 0 The left join preserves customer 2 with null values for the missing order columns. COUNT(
) counts that result row. COUNT(o.order_id) counts only non-null order IDs. The distinction matters because order_id is assumed non-null for every real order. This query is illustrative and has not been executed against a database for this post. Run it in a development environment before adapting it. Keep the population you intended Adding this condition after the join changes the question: WHERE o.status = 'paid'
It removes rows where the right-hand status is null, so customers without a matching paid order disappear. That may be correct for “customers who paid,” but not for “all customers and their paid-order counts.” To retain all customers, either keep the conditional aggregate shown above or restrict the join itself: LEFT JOIN orders AS o
ON o.customer_id = c.customer_id
AND o.status = 'paid'
Then count the non-null order key. These are alternative query shapes, not instructions to paste both joins into the same statement. Check the assumptions before trusting the count The customer input should have one row per customer for this report. Duplicate left-hand keys multiply matches. Count a right-hand column guaranteed non-null for a real match. A nullable description is not an order counter. Joining order items afterward changes the grain again. Aggregate or validate that relationship before interpreting rows as orders. COUNT(DISTINCT order_id) can express a distinct-order metric, but it can also conceal a mistaken join. Explain the metric before reaching for it. Test one customer with no orders, one with only pending orders and one with several paid orders. Verify that every intended customer remains present and that zero means zero matching orders, rather than a missing source feed. References: PostgreSQL aggregates and table expressions and outer joins. Saravana Kumar | TechX Personal website · Instagram AI disclosure: Original tutorial generated by an AI assistant without human editing. Results are reasoned expectations, not a claim of database execution.

Powered by Readybot.io

**DEV Community** ## SQL COUNT After a LEFT JOIN: Why …

DEV Community

Why was this 837 rejected? Tracing 999 and 277CA responses back to the claim

A claim batch went out three months ago. Some claims were paid, some were denied, and a few simply never came back. No remittance, no denial, nothing. Before anyone calls the payer, there is a cheaper question: did those claims ever get past the front door? Two files answer that, and neither one looks like a remittance: the 999, which says whether a whole transaction set (one ST/SE block of claims) was accepted, and the 277CA, which reports claim by claim whether each one was accepted or rejected before adjudication. A claim rejected at either stage never produces an 835. So "no remit" and "rejected" look the same in your aging report unless you connect the files. This post shows how to do that by hand, then with a free CLI I wrote. Everything below uses synthetic files. What a rejection looks like on the wire A 999 answers one 837 transaction set at a time. Each set gets an AK2 segment naming it (the transaction type and the ST control number from your file), then an IK5 segment with the verdict. Optional IK3/IK4 segments point at the segment and element that failed. This is a synthetic 999 where I rejected the second set: AK1HC1005010X222A1~
AK2
8370001005010X222A1~
IK5A~
AK2
8370002005010X222A1~
IK3CLM1423008~
IK4210287NOTAVALIDID~
IK5R5~
AK28370003005010X222A1~
IK5
A~
...
AK9P554~
IK5A is accepted, IK5R is rejected, and AK9P says the group was only partly accepted: 5 sets received, 4 accepted. The whole second set, all its claims, was bounced. A 277CA works at the claim level. Each claim gets a TRN segment carrying your patient control number (the CLM01 you sent), followed by an STC status segment: TRN2PCN00000032~
STC
A3:2120260102U224.56~
The first element of STC starts with a category code (here A3). Which categories mean "rejected" depends on your payer's companion guide, so check yours. x12diff treats A3, A4, A6, A7 and A8 as rejections by default and A0, A1 and A2 as acceptances, and both lists are configurable in the profile. The by-hand method Open the 999. For every AK2 with an IK5
R, note the ST control number. In your 837 file, find the set with that ST02. Every CLM between that ST and its SE was in the rejected set. Open the 277CA. Collect every TRN02 whose STC category is a rejection. Those are claim IDs, which are CLM01 from the 837. Remove all of those from your "unpaid" list. What is left really is waiting on the payer. That works for one batch. It stops working when you have a month of files, several sets per file, and resubmissions that reused control numbers. Which brings the first trap. Trap: control numbers are not unique ST02 and GS06 are only unique inside their interchange. If you start numbering at 1 in every file, a 999 for ST02 = 0002 could belong to a dozen different 837s. Matching safely needs more than the number: the group control number, the version string, the date, and who sent it. Being sure which file a 999 answers is the hard part, not reading the IK5. Also: a 277CA acceptance can arrive after an earlier rejection (you fixed and resent the claim). The newest status should win. If you just take the first STC you see, you flag claims that are fine. Doing it with x12diff x12diff is a free command-line tool for 834, 835, 837 and the acknowledgement files. Disclosure: it is mine, it is MIT licensed, and it runs entirely offline. There is a paid desktop version planned, but everything in this post is the free CLI. Give recon the 837, your 835s, the 999 and the 277CA in any order. It recognizes each file by its contents: $ x12diff recon claims.837 remits_1.835 remits_2.835 ack.999 status.277 --asof 20260601 --csv recon.csv --show 0
Files: 5 837 claims: 200 in 5 sets 835 claim entries: 185 on 3 checks
Acknowledgement sets: 1 (1 837 sets rejected) 277CA claim statuses: 26
PAID: 99
PARTIAL: 37
DENIED: 14
PATIENT_RESP: 9
REVERSED: 8
ADJUSTED: 3
UNMATCHED: 18
REJECTED: 12
ORPHAN: 1
Charged (837): 50866.73 Paid (m...

Powered by Readybot.io

**DEV Community** ## Why was this 837 rejected? Tracing 999 …

DEV Community

Dream Cycle: a chat that remembers, then sleeps

I built Dream Cycle for the HydraDB Hackathon. It is a chat that saves every message as a memory in a HydraDB graph. Then you press Sleep, and it cleans the memory: it merges duplicates, drops small talk, and rewrites contradictions. Ask the same three questions before and after Sleep, and the answers get better. What I built Dream Cycle is a simple web app. You chat with it, and every message becomes a memory. I seeded it with 200 fake memories: 120 duplicates, 40 bits of noise, and one contradiction. The contradiction is the point. One memory says "I love coffee." A newer one says "You quit coffee." Before Sleep, the app answers "Do I like coffee?" with the old line. After Sleep, it answers "You quit coffee." That is the whole demo in one flip. How HydraDB helped I used HydraDB's open-source graph engine, self-hosted. Every memory is a real (:Memory) node in the graph, not a row in a table. Every read and write is real Cypher over the graph's HTTP API. Sleep is not a filter on a list. It runs Cypher to find duplicate memories, merge them, drop the noise, and write the cleaned version back as a node. The memory count drops from 200 to 39, and the graph does the work. I also worked with selfgraph, HydraDB's temporal memory library, for the $50 side bounty. I audited it and found a real bug: when you restore a source, the old text comes back in historical reads, even for times when the replacement was the true text. Finding that bug taught me how the temporal layer is supposed to work, and it made the Dream Cycle design sharper. The technical challenges, and how I solved them No free hosted workspace. The HydraDB dashboard only showed paid plans, and there were no free credits for the hackathon. The answer from Discord was to use the open-source engine. So I built the graph-node from source. It needs Rust, a Cypher parser library, and GraphBLAS, and the compile takes a while. Once it was running, I had a real graph database on my own machine. No semantic search. The open-source build has no embeddings, so retrieval is Cypher over stored text, word match, not meaning. I did not pretend otherwise. The demo works because the memories are exact text and the questions match them. The Render deploy kept failing. The first build failed because an install script needed a folder name. The second failed because the Rust build needed a feature flag called server-runtime, or the binary would not compile at all. I fixed both lines in the Dockerfile, redeployed, and the app went live. The binary was too big to push. The compiled graph-node is 74 MB, and the GitHub API refuses files that large. So the Dockerfile builds it from source at a pinned commit instead of shipping the binary. A race bug. The page loading twice seeded the database twice. I added a lock so only one seed can run at a time. The video. Loom's free account cannot upload a pre-recorded video, so I am sorting a different link for the demo video. What I would be honest about Only the coffee answer flips. The memory map is a row of dots, not a real graph picture. Sleep is one-shot, you reset before a fresh run. But the core works end to end: 200 memories in, Sleep, 39 out, and the answers improve. That is what I set out to prove. Links Live app: https://dream-cycle.onrender.com/ Repo: https://github.com/skymiller1509-blip/dream-cycle The demo video is in the repo under demo/dream_cycle_demo.mp4. @hydra_db

Powered by Readybot.io

**DEV Community** ## Dream Cycle: a chat that remembers, then …

DEV Community

Retrieval Architecture for Engineering Docs: Versioning and Freshness Boundaries

Short answer: use staged retrieval with explicit collections, bounded queries, and traceable source context. Infrai is a useful fit for the collection and query stages when a self-describing REST contract keeps a small tool moving. For internal engineering documentation that contains near-duplicate media records, versioning and deletion rules matter more than picking a fashionable vector index. I build CLIs and SDK glue for other developers. My first benchmark is time-to-first-call, then I measure the boring things: stale-hit rate, p95 query time, and how often a citation points at the wrong revision. A retrieval demo can look perfect while an old runbook still wins against the current one. That is a data-handling failure, not a ranking mystery. What should a retrieval architecture for internal engineering documentation version? Start with a retrieval contract. Each chunk needs a stable document id, a revision id, source URI, effective timestamp, and deletion state. The query contract returns the chunk plus that context, so the answer layer can show exactly which revision it used. Keep the user-visible answer mapped to this contract before choosing storage. For the media workflow, I put near-duplicate records in an explicit collection for the corpus and keep revision metadata alongside each vector. A changed transcript creates a new revision. The indexer then marks the previous revision inactive and removes deleted records from the collection. “Overwrite in place” is tempting; it also makes an audit trail disappear. In practice, a release can change a title, transcript, rights flag, and region at different times; the ingestion job therefore records each source event, derives one revision key, checks that every chunk carries the same key, and only then switches the query filter. That extra bookkeeping feels slow on day one, but it prevents a mixed answer assembled from two incompatible revisions. Region and processor boundaries belong in the contract too. The retrieval service can select a collection and enforce metadata filters, but it cannot grant a provider permission to retain source audio or satisfy a residency clause. Keep raw media in the system that owns those agreements. Send only the text or embeddings that the contract permits, and record the processor and region decision with the ingestion event. One sentence that keeps the design honest: retrieval can enforce which indexed revision is eligible; it cannot turn an unapproved processor into an approved one. Ship the contract first. A small build log: chunk, index, query Chunking is the decision axis here. Split engineering documents at headings and version boundaries, then keep a small overlap for references that cross a boundary. For media-derived records, a chunk should carry the recording id and transcript revision, not just a blob of text. Near-duplicates become distinguishable when the metadata says which revision and capture window produced them. The first pass is bounded and filtered. Query the collection for a modest candidate count, then compare semantic similarity and revision freshness in a second stage. Never run a global top-20 and filter access afterward; the intermediate component would already have seen records outside its trust boundary. Below is the transport skeleton I use in a TypeScript CLI. The payload objects are validated against the live discovery schemas before this function is called. That keeps the sample runnable without inventing request fields, while the helper still handles explicit methods, timeouts, status checks, and 429 backoff. const baseUrl = "https://api.infrai.cc/v1";
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function post(url: string, body: unknown): Promise {
for (let attempt = 0; attempt < 4; attempt += 1) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8_000);
const response = await ...

Powered by Readybot.io

**DEV Community** ## Retrieval Architecture for Engineering Docs: Versioning and …

DEV Community

Chrome Zero-Day CVE-2026-125350: How to Update Chrome and 6 Checks for Extension Developers

Updating your own Chrome takes a minute. The part that takes longer is every other Chrome you forgot you have. None of this is a reason to ship anything in a hurry. You read the headline between two tasks. Another Google Chrome zero-day, already used in attacks. You open the menu, see a small "Relaunch" button, think "later", and go back to your code. The laptop stays open for the rest of the week. CVE-2026-125350 is a high-severity Chrome vulnerability: a memory bug (a "use-after-free") in the part of Chrome that reads XML. Google fixed it with a Chrome security update on 9 November 2026, in Chrome 157.0.8103.50 for Linux and 157.0.8103.50/.51 for Windows and Mac, and wrote one short line about it: an exploit for this bug exists in the wild. The security press counts it as the eighth Chrome zero-day used in attacks this year. A smaller question helps more than the headline: how many copies of Chrome do you really run? For an extension developer the answer is rarely one. Go through the list in order. The first four are checks for today; the last two are for the rest of the week. 1. Check your Chrome version "I have auto-update" is a setting, not a version number. If you are asking "what version of Chrome do I have?", open this page and read the number: chrome://settings/help
On Linux the terminal shows the installed build (the running one may still be older until you restart): google-chrome --version
There is one more detail. On 11 November Google shipped a second update, 157.0.8103.78 (.79 on some Windows and Mac machines), with 83 more security fixes, five of them rated Critical. So the fix for the zero-day is in .50, but the latest Chrome version at the time of writing is .78, and that is the number you want to see. 2. Update Chrome, then relaunch it How to update Chrome: open Help → About Google Chrome and wait for the download to finish. On Windows and Mac, Chrome usually does this by itself in the background; on Linux the update arrives with your system updates. It starts using it only after a restart. A downloaded update protects nobody while the old browser is still running. Developers are a bad case here. We keep one window open for days, with forty tabs. Picture a freelance developer whose Chrome downloaded the update on Monday. They never closed the window, so on Friday they are still on the old build. Click "Relaunch". 3. Count the other Chromes on your machine This is the step that took me the longest. On my work machine I found more browsers than I expected: Chrome Beta and Canary, which I use to test upcoming changes an old portable copy in a folder for reproducing bug reports the browser that my test tool downloaded for itself The last one is easy to forget. Automation tools such as Playwright and Puppeteer download their own browser build. It does not update with your Chrome. It changes only when you update the tool. This part is a guess: I have not seen an official statement about these builds and this bug. They are built from the same code, so I treat an old one as unpatched. Check which browser version the tool ships, update the tool when a release with 157.0.8103.50 or later is out, and until then do not use that browser to open pages you do not control. 4. Check the other browsers your extension lives in If you publish in more than one store, you also run more than one browser. Each of them ships the fix on its own schedule. Edge. Microsoft's release notes say Edge 157.0.4341.46, released on 9 November, contains the fix. Brave, Opera, Vivaldi. I did not find a clear statement for each of them, so I give no version numbers here. Open the "About" page in each browser and read the vendor's release notes. Firefox uses a different engine. I found no report that it is affected. 5. Watch your store numbers for a week, and do not ship in a panic Google describes it as a bug in the browser itself. Nothing I read says extension code needs to change because of it. An emergency release "just in case" brings mor...

Powered by Readybot.io

**DEV Community** ## Chrome Zero-Day CVE-2026-125350: How to Update Chrome …

DEV Community

Adding Git repo & test into Rust Studio IDE

Ruststudio is a free IDE for Rust that is itself written in Rust: one native program of about 10 MB, Version 0.1.6 moves three things into the editor that used to need a terminal: Git, running single tests, and adding packages. This post is less a feature list (the release article has that) than the three design decisions I would defend, and the places where I am not sure I got it right. Decision 1: do not implement Git, run it The obvious route is a Git library. I chose to run the git the user already has, for one reason: a Git panel is only trustworthy if it does exactly what git in the terminal would do. Hooks run. .gitconfig applies. Your credential helper signs you in, which means the IDE never sees, asks for or stores a password. When Git refuses, the panel shows Git's words and does not retry with a force flag. The cost is that you depend on Git's output, and you have to be careful with names (spaces, non-English letters, renames, binary files). The payoff is that everything the panel does is something you could have typed yourself, including staging one hunk or only the lines you click. One more rule I stuck to: the view never polls. It refreshes when something happens (a command, a save, a build, coming back to the window). A timer that runs git status every second looks responsive in a demo and punishes large repositories. Not done: a side-by-side diff, stash, interactive rebase, a commit graph. The Terminal does those, and it is the same Git. Decision 2: the test list comes from Cargo, not from my parser Finding tests by scanning source for #[test] is easy and wrong: macros generate tests, attributes differ (#[tokio::test], #[rstest], #[test_case]), and doc tests are not functions at all. So there are two sources, used for different jobs: The margin icons come from the source, so they appear as soon as a file is open, with no build. They are a convenience and they recognise the common attributes. The Test Explorer shows what Cargo reports (package, test binary, module, test, doc tests, ignored tests), so it can disagree with the margin when macros are involved, and when it does, Cargo is right. Running is your own cargo test with the toolchain and configuration you chose. Results are shown per test while the run is still going, and a failure gets its panic message and file:line under it and an entry in the Problems panel, so a failing test reads like a compile error. Debugging a test builds the test program and starts it under the debugger with that one test selected, so breakpoints in the test and in the code it calls are hit. (On Windows with the MSVC toolchain this uses PDB symbols through lldb-dap.) Known gaps: no coverage, no per-test timing, doc tests can be run but not debugged, and tests generated by macros get no margin icon. Decision 3: hints from rust-analyzer, shown but never written Inlay hints (the type after a let, the parameter name at a call, the types along a method chain) are drawn dim inside the code. They are not part of the buffer: the caret steps over them, they cannot end up in a diff, and View > Toggle Inlay Hints turns them off. The same mechanism shows variable values at the end of the lines while the debugger is stopped. One thing I noticed while taking these screenshots: on one machine rust-analyzer could not resolve the standard library and showed {unknown} for some types. The hints are only as good as what rust-analyzer resolves, and I would rather say so than choose only the screenshots that look perfect. Also in 0.1.6 Package sources you can add (any NuGet V3 feed or Cargo sparse registry, alongside nuget.org, crates.io and the Ruststudio registries), publishing to rupm or NuGet from the IDE with a confirmation that says publishing is public, and Go to Implementation (Shift+Alt+F12). Native iOS apps are in development and are not part of this release.

Powered by Readybot.io

**DEV Community** ## Adding Git repo & test into Rust …

DEV Community

How to Write a README That Explains Your Project in 30 Seconds

Most people who open your README are deciding whether to read it. They came from a search result, a link in an issue, or a dependency list, and they will give the page a few seconds. In that time they want three answers: what is this, is it for me, and how do I try it. A README that answers those three in its first screen does most of its work before anyone scrolls. This article is a structure for that first screen, with an example built around a small command line tool, and the parts that belong further down. The tool, logtrim, is invented for this article; the commands show the shape of a README, not a real package. What does a stranger need first? Think of the reader who has never heard of the project. They do not know your internal names, the history of the repository, or why it was started. They know their own problem. So the first screen answers, in order: What it does, in one sentence, in words the reader would use for their own problem. Who it is for, or the situation where it helps. What using it looks like, as the shortest real example. How to install it, as one command if possible. Everything else (configuration, architecture, contributing, the changelog, the badges) is for people who already decided to stay. The one-sentence description The first sentence is the hardest and the most important. A useful test: could someone who has the problem recognise it in your sentence without knowing your field's vocabulary? Weak versions describe the implementation or praise it: A fast, modern, extensible CLI built with Rust for working with log files. This says what it is made of and how the authors feel about it. It does not say what happens when you run it. A stronger version describes the result: logtrim cuts large log files down to the lines that matter: the errors and the lines around them. The reader now knows whether this is their problem. If they do not have large log files, they can leave, and that is also a good outcome. Show the shortest real example After the sentence, show it working. Not a full tutorial: one input and one output, copied from a real run. $ logtrim app.log --context 2
[12:04:11] INFO request started id=8812
[12:04:11] INFO calling payment service
[12:04:12] ERROR payment service timeout after 5000ms
[12:04:12] INFO retry 1 scheduled
[12:04:13] INFO retry 1 ok
A reader understands an example faster than a list of features, because they can see the shape of the result and compare it with their own case. Keep the example small enough to fit on the screen with the description above it. If the real output is long, cut it and say so in a comment. Install in one line, then point onward cargo install logtrim
If installation needs more than one step, put the most common path here and link to the full instructions further down. The first screen is for trying, not for every operating system. Before and after Here is a typical README opening, shortened: # logtrim
[badges: build, coverage, version, license, downloads]
logtrim is a modern, blazing fast log processing toolkit. It was started in 2023 to solve problems we had at work and has grown into a flexible framework with plugins, multiple output formats and a configuration system.

Table of contents

...
A stranger reads the badges, a history, and adjectives, and still does not know what the tool does to a log file. The same opening, rewritten: # logtrim
logtrim cuts large log files down to the lines that matter: the errors and the lines around them.
Useful when a log is too long to read and grep shows the error without its context.
(example run)
(install command)
Nothing was invented. The facts were already in the old version; they were in the wrong order and hidden behind words that describe the authors rather than the tool. What goes further down Once the first screen is right, the rest of the README can be ordered by how often people need it: Usage: the common options, each with a short example. Configuration: only if there is any. How it wo...

Powered by Readybot.io

**DEV Community** ## How to Write a README That Explains …

TechCrunch

Flaming Hydra’s new Kickstarter campaign rethinks e-book ownership and digital lending

A new Kickstarter campaign aims to usher in the “return of the human internet.”

Powered by Readybot.io

**TechCrunch** ## Flaming Hydra’s new Kickstarter campaign r

DEV Community

Automating Construction Invoice Workflows with n8n, Gmail and Notion

automation #javascript #productivity #showdev Managing construction operations involves more than tracking projects. Businesses also need to manage supplier invoices, project costs, equipment hire and payment statuses. I explored these challenges through Construction Operations Automation, a portfolio prototype using Notion, n8n, Gmail and JavaScript. The Problem When invoices arrive through email, manually entering them into a project database creates several risks: Duplicate invoices Missing supplier information Incorrect payment statuses Inconsistent monthly reporting Poor visibility of project expenditure My objective was to design a workflow that reduces repetitive administration. The Solution The prototype connects email processing with structured Notion databases. The workflow follows this sequence: Gmail receives an invoice. n8n retrieves the message. JavaScript extracts invoice information. Required fields are validated. The workflow checks for an existing invoice. A project cost record is created or updated. The supplier-month summary is created or updated. The design also considers changes such as an invoice moving from Unpaid to Paid. Example Invoice A fictional test invoice contains: Invoice reference: TEST-001 Supplier: Demo Building Supplies Project: Sample Residence Amount: 350 Status: Unpaid The invoice reference helps the workflow identify existing records and avoid unnecessary duplicate creation. What I Tested The repository documents tests involving new invoices, duplicate handling, payment-status updates, missing fields and supplier-month summary creation. Important Limitation This is a sanitized portfolio reconstruction, not a production deployment. One important improvement is still needed: monthly supplier totals should be recalculated from all relevant project cost records. Without that, multiple invoice updates could produce incorrect aggregate values. Other future improvements include error retries, audit logs, attachment handling and production monitoring. What I Learned This project helped me explore the relationship between business processes, database design and workflow automation. A working automation is not just about connecting applications. It also needs reliable validation, consistent records and careful handling of repeated events. GitHub: https://github.com/tasnem-tech/construction-operations-automation Project website: https://tasnem-tech.github.io/construction-operations-automation/ I'm interested in how other developers approach reliable invoice processing and no-code/low-code automation.


Powered by Readybot.io

**DEV Community** ## Automating Construction Invoice Workflows with n8n, Gmail …

DEV Community

A stranger tested my SaaS and found my Free plan was hiding the product

I run Formkove, a form backend. You point your HTML form at an endpoint and submissions go to your inbox, a webhook, Slack and so on. Paid plans add AI that tags each submission, reads the sentiment and routes it to the right place. A few days ago someone found Formkove in a Reddit thread, signed up, hosted a real form, sent submissions through it and emailed me four things they'd change. I hadn't asked. Near the end they wrote "the backend itself worked really well for me, so this is mostly about letting people see that sooner." They were right. Nothing big was broken, people just couldn't see the part I charge for. I shipped all four changes the same day. 1. Free users never saw the AI My homepage sells sorting by sentiment and your own tags. But on Free the AI switch was disabled, so someone deciding on $12 a month had never seen one tagged submission. They suggested AI on the first ~25 submissions. I went with a real trial: every Free account gets Pro's AI (tags, sentiment, routing and up to 3 destinations like Slack or webhooks) for the first 50 submissions or 30 days, whichever comes first. No card, nothing to turn on. Most of the work was deciding what counts: The 30 days start at your first real submission, not at signup. People often take a week to wire up the form, and a signup clock would burn half the trial before they see anything. Spam and test submissions don't use it up. Otherwise bots could eat the 50 before a real lead came in. New forms get starter tags (lead, support, feedback). The AI tags against your own list, so without these the first submission would have no tags. Tags stay after the trial ends. New submissions go back to the Free spam check. Existing Free users got it too. Anyone who has ever paid can't get one, so cancelling doesn't buy a fresh trial. The sidebar shows how much is left, and each notification email has one line with the count. The upgrade nudge only shows up near the end. 2. There was no way to send a test The empty Submissions page had one button, "Open setup", and Setup had the endpoint and snippets but no way to try it. They had to host a form somewhere else to see anything. With a trial that's worse, since the first thing you want is to watch the AI work. Now there's a "Send test submission" button on both pages. It sends a sample lead through the real pipeline (spam check, AI, email, webhooks) and the row is marked as a test everywhere. They also suggested a curl example. I skipped it because we block curl at the edge, a lot of the bot traffic on form endpoints comes from it. 3. The AI said "Neutral" when nobody asked it This was a real bug. With AI off, every submission still showed a "Neutral" badge, including "Could you quote a redesign... Budget about 4000 USD" and casino spam. As they put it, that makes the sentiment model look weak to someone deciding on Pro. The model never looked at sentiment. Free gets a cheap spam-only check, but the parser returned the same shape as a full analysis, and that shape needed a sentiment: return {
spam_score: Math.round(spamScore * 1000) / 1000,
is_spam: spamScore >= SPAM_THRESHOLD,
assigned_tags: [],
sentiment: "neutral",
}
"neutral" was a placeholder to keep the type happy, and the database column had the same default. The fix was sentiment: null, a migration so the column allows it, and a locked "Sentiment and tags · Pro" chip where the badge was. A fake label on casino spam is the last thing you want on the day you launch a trial that's meant to show off the AI. 4. The notification email led nowhere The "New submission" email only linked to the homepage. Now every notification has a "View submission" button that opens that exact submission, which during the trial is where you see the tags. They also suggested a line like "Pro would have tagged this: lead · positive". I skipped it. I'd just removed one made-up label and the trial shows real tags anyway. What I took from it Error tracking can't flag a value that's wrong but ...

Powered by Readybot.io

**DEV Community** ## A stranger tested my SaaS and found …

DEV Community

One Brief, 63,000 Words, 42 Illustrations, and a Working Flipbook: A Qwen4 Case Study

One brief. 40 chapters. 42 illustrations. A working browser-based flipbook. What happens when you give a newly released flagship model a project that can't be completed in one shot? When a new flagship model appears, the usual first tests are familiar: ask it a hard reasoning question, throw a coding benchmark at it, or have it build a small app. I wanted to try something less tidy. I gave Qwen 4 Preview a project with several kinds of work tangled together: write a complete children's mystery, create a consistent illustration system for it, and turn the result into an interactive book that actually works in a browser. The output was: A 63,258-word novel across 40 chapters. 42 SVG illustrations built around a reusable library of nine characters. A single-file, 652-page HTML flipbook. A headless browser run that reported zero runtime errors and zero text overflow. The run took about 1 hour and 42 minutes and involved 242 assistant turns. I ran the model through Jouzu, a coding-agent harness. The source repository contains the prompts and generated artefacts. You can open the finished flipbook here. This isn't a benchmark, and I'm not claiming that one run settles anything about the model. It is a practical look at how Qwen 4 Preview handled a long-running task where the requirements had to survive across multiple stages. Why this particular test? The project is called Pip Parker and the Bureau of Lost Things. Pip is a boy who labels everything. When his brother's stuffed dinosaur goes missing, the trail leads to a bureaucratic animal office that files the town's lost things. There's a mystery to solve, a set of clues to pay off, and a midpoint reframe: the apparent thief isn't stealing things; he's saving them. A children's mystery makes a useful stress test because continuity failures are easy to spot. A character can't suddenly know something they haven't learned. An object can't be in two places at once. A clue introduced early has to matter later. And the ending has to resolve what the opening set in motion. Then I added two more layers. The book needed illustrations, but not 42 unrelated images. It needed a consistent visual language: nine reusable characters, an eight-colour palette, and SVG artwork that reused the same character definitions. Finally, it needed to become a real reading experience. Not a PDF export or a long HTML page, but a flipbook with page turns, navigation, bookmarks and controls. That combination made the task interesting. The agent had to move between narrative generation, structured visual assets and application development without treating them as three unrelated jobs. The brief: constraints first I deliberately made the brief specific. I didn't want the model to decide halfway through that 20 chapters were enough, change the cast, or quietly simplify the illustration requirements. The novel specification called for: 40 chapters, each between 1,400 and 1,700 words. A target of roughly 60,000 words. A fixed cast and timeline. Five explicit plant-and-payoff pairs. A continuity ledger tracking day-by-day events and object ownership. A fixed final sentence. Kid-safe content rules: no death or gore, and a lonely rather than evil antagonist. The plant-and-payoff requirements were especially useful. A detail introduced in one chapter had to become meaningful several chapters later. The model couldn't just make each chapter locally plausible; it had to preserve information for future use. The illustration brief was equally constrained: A nine-character SVG library. A locked eight-colour palette. One cover illustration and one illustration for each of the 40 chapters. The same SVG definitions reused across the plates. The point of specifying these constraints wasn't to make the prompt impressive. It was to make success and failure observable. It took one magic prompt The prompts are preserved verbatim in PROMPT.md. 1. Write the book and create the art system. The initial brief produced the novel, the i...

Powered by Readybot.io

**DEV Community** ## One Brief, 63,000 Words, 42 Illustrations, and …

DEV Community

Analysing 900 IT Help Desk Tickets with Python and SQL

Tags: #python #sql #datascience #beginners How can an IT support team identify recurring problems, measure service quality and improve resolution times? I explored these questions through my Help Desk Ticket Analysis project using Python, Pandas, SQL and Matplotlib. The Dataset The project uses 900 synthetic IT support tickets from 2025. The dataset contains information about issue categories, priorities, support teams, response times, resolution times and service-level agreement (SLA) performance. Because the data is synthetic, the findings demonstrate analytical techniques rather than the performance of a real organisation. Data Cleaning Before calculating performance metrics, I worked with common data-quality problems, including: Duplicate ticket identifiers Missing values Inconsistent support-team names Incorrect timestamp formats Inconsistent issue categories One important decision was not to automatically discard every incomplete ticket. A record missing an analyst name may still contain useful resolution and SLA information. Key Results The repository reports the following results for its synthetic dataset: Metric Result Total tickets 900 Average first response 0.91 hours Average resolution time 7.74 hours SLA compliance 70.3% Average satisfaction 4.03/5 The most common issue category was Application Error. Why SLA Compliance Matters A low average resolution time can look positive while hiding serious delays for high-priority tickets. That is why I compared multiple indicators rather than relying on one overall average. The analysis considers ticket volume, issue categories, resolution time, SLA compliance and customer satisfaction. Tools Used Python and Pandas for cleaning and aggregating records. SQL for operational reporting queries. Matplotlib for visualising ticket patterns. Jupyter Notebook for exploratory analysis. What I Would Improve Future extensions could include an interactive Power BI dashboard, ticket backlog analysis, reopen rates, first-contact resolution and automated ticket categorisation. Conclusion This project demonstrates how data analysis can support IT service management. The main lesson was that operational performance should be evaluated through several connected metrics. GitHub: https://github.com/tasnem-tech/help-desk-ticket-analysis Which service desk KPI do you find most useful: response time, resolution time or SLA compliance?

Powered by Readybot.io

**DEV Community** ## Analysing 900 IT Help Desk Tickets with …

DEV Community

Analysing UK Retail Sales and Profitability Using Python and SQL

Tags: #dataanalytics #python #sql #datascience Revenue tells us how much a business sells. Profit tells us how much value it keeps. Understanding the difference is an important part of business data analysis. I explored this through my UK Retail Sales Performance Analysis project. Project Overview The project analyses 2,500 synthetic UK retail transactions from 2025. The dataset covers five regions, four product categories and two sales channels. My objective was to practise transforming transaction records into meaningful business metrics. Analytical Workflow The workflow includes: Importing raw transaction data. Checking missing values and duplicates. Standardising inconsistent fields. Calculating revenue and profitability metrics. Running SQL queries. Creating visualisations. Interpreting business results. The raw dataset intentionally contains minor data-quality issues to demonstrate cleaning techniques. Key Results The repository reports: KPI Result Total revenue £320,707 Total profit £116,331 Profit margin 36.3% Return rate 9.2% Top revenue region Midlands Top profit category Electronics These are results from simulated transactions, not real UK retail market statistics. Example SQL Query One of the included queries calculates regional revenue: SELECT
region,
ROUND(SUM(revenue_gbp), 2) AS revenue
FROM retail_sales
GROUP BY region
ORDER BY revenue DESC;
This produces a ranked view of revenue contributions by region. Turning Analysis into Decisions The analysis raises several business questions. Should management investigate why the Midlands performs strongly? Are high-revenue products also generating strong profits? Do discounts improve sales volume at the expense of profitability? Could high return rates reduce the value of certain product categories? The synthetic dataset cannot establish real-world answers, but it provides a useful environment for practising analytical reasoning. Future Improvements I would like to extend this project with Power BI, customer segmentation, forecasting and automated reporting. Conclusion This project helped me connect Python and SQL analysis with business decision-making. The most useful takeaway was that revenue, profit, returns and discounts need to be examined together. GitHub: https://github.com/tasnem-tech/uk-retail-sales-analysis What business metric would you investigate first when reviewing a retail dataset?

Powered by Readybot.io

**DEV Community** ## Analysing UK Retail Sales and Profitability Using …

DEV Community

I Built a Self-Hosted 4-in-1 E-Commerce Engine in Laravel (Retail, Wholesale, B2B & Subscriptions)

Hey Laravel devs 👋 I’m Stephan—an independent PHP/Laravel developer, and this is my first time posting in the Laravel community here on DEV. After years of building custom WordPress plugins and client Laravel apps, I wanted to start sharing the architecture behind what I'm building and connect with other Laravel builders. The Problem I Wanted to Solve Whenever a client or solo founder wants to launch an online store, they usually hit a wall: either pay escalating monthly SaaS fees and transaction cuts, or wrestle with bloated legacy plugins just to handle basic commerce rules outside of standard retail. I wanted a lightweight, self-hosted Laravel foundation that could handle four distinct commerce models out of the box without duct-taping a dozen third-party packages together: Retail: Standard direct-to-consumer storefront and cart Wholesale: Tiered bulk pricing and minimum order rules B2B: Account-based purchasing workflows Subscription Boxes: Recurring billing cycles Under the Hood of ShopKeeprr v1.1 I just shipped ShopKeeprr v1.1 as a free, self-hosted core engine. A few of the Laravel implementation details I enjoyed building most: WordPress-Style In-App Marketplace: Instead of forcing store owners to drop into SSH and run Composer commands to add features, I built an in-app extension installer using native HTML overlays, package archive extraction, and SHA checksum verification. Zero-CLI Web Installer (install.php): Many self-hosted users still deploy on cPanel/shared hosting where terminal access is limited or intimidating. ShopKeeprr includes a browser-based setup wizard that validates PHP extensions, writes the .env file, generates APP_KEY, and runs migrations automatically. Unified Payment Webhooks: Built-in Stripe and PayPal webhook handlers engineered to stay consistent across one-time retail carts, B2B invoices, and recurring subscriptions. Check Out the Code or Demo The core engine is completely free to download and self-host: GitHub: https://github.com/DevByStephan/ShopKeeprr Live Demo: https://demo.shopkeeprr.com Since I'm actively building out the extension ecosystem, I'd love feedback from fellow Laravel developers—how do you usually structure modular plugin/extension architectures in your Laravel apps?

Powered by Readybot.io

**DEV Community** ## I Built a Self-Hosted 4-in-1 E-Commerce Engine …

DEV Community

Analysing 68 Years of Atmospheric CO Data with Python

Tags: #python #datascience #climatechange #dataanalytics One of the most interesting things about data analysis is the opportunity to explore real scientific questions. For my Keeling Curve Climate Data Analysis project, I worked with historical atmospheric carbon dioxide measurements and global temperature data. What Is the Keeling Curve? The Keeling Curve records the long-term increase in atmospheric carbon dioxide concentrations, using measurements that began at Mauna Loa in 1958. The project explores data from March 1958 through May 2026, alongside global temperature anomaly data. Project Objectives I wanted to investigate: How atmospheric CO₂ concentrations changed over time Whether the average growth rate increased How seasonal patterns appear in the data How CO₂ and global temperature trends compare How to communicate scientific findings through visualisations Tools and Data The project uses Python, Pandas, NumPy, Matplotlib and Jupyter Notebook. Its documented data sources include NOAA atmospheric CO₂ measurements and NASA GISS temperature anomaly data. Key Findings The repository reports: Measure Result CO₂, March 1958 315.7 ppm CO₂, May 2026 432.3 ppm Total increase 116.6 ppm Percentage increase 36.9% 1960s annual growth 0.86 ppm/year 2020s annual growth 2.67 ppm/year The analysis also reports a correlation of r = 0.966 between overlapping CO₂ and temperature time series. This correlation should be interpreted carefully. Two strongly trending series can produce a high correlation, and correlation alone does not establish causation. A Data-Quality Challenge One interesting challenge involved the imported CO₂ dataset. The expected column structure did not match the retrieved data fields. Instead of immediately analysing the imported table, the project involved inspecting the structure, correcting column mappings and validating the results. This reinforced an important lesson: Never assume that imported data is analysis-ready. Limitations Mauna Loa is a monitoring location, not a direct measurement of every location on Earth. The comparison also uses different observational datasets with their own measurement and modelling assumptions. Further analysis could examine detrended relationships, uncertainty and seasonal adjustment. Conclusion This project helped me practise time-series analysis, data validation and scientific visualisation using real environmental data. GitHub: https://github.com/tasnem-tech/keeling-curve-climate-data-analysis What scientific dataset would you recommend for a future Python analysis project?

Powered by Readybot.io

**DEV Community** ## Analysing 68 Years of Atmospheric CO Data …

DEV Community

Node.js Message Processing Rate-Limiting: Choosing a Queue Service

Short answer: To implement rate-limiting in Node.js message processing, use a queue service for delayed work and keep a durable per-destination token bucket beside the delivery record. The queue moves the message. The limiter decides when processing may start. Choice Good fit Recovery trade-off Queue plus per-destination bucket Many recipients with separate quotas More state to inspect One global concurrency limit One downstream quota One failing recipient affects others Scheduled database polling Low volume and simple recovery Claim and polling logic become yours I would use the first row when the operational question is “which destination is aging?” That answer is more useful than a single queue-depth number. The cost is clear state and an atomic limiter. How should a Node.js queue service implement rate-limiting for message processing? The queue service owns visibility, delayed re-enqueueing, and worker movement. The application owns the delivery record, recipient policy, and idempotency decision. A timeout is an unknown result, not proof that the receiver did nothing. For one marketplace event sent to two destinations, keep separate delivery records. The event ID identifies the business event; the delivery ID identifies one destination-specific job. Reusing one status for both would make an outage at one destination block recovery. Keep it boring. type WebhookDelivery = {
deliveryId: string;
eventId: string;
destinationId: string;
state: 'pending' | 'in_flight' | 'succeeded' | 'retryable' | 'dead';
attempt: number;
nextAttemptAt: string;
idempotencyKey: string;
};
I first treated a delayed queue as the whole solution. That was the wrong boundary. Delay says when a job can be visible again; it does not say whether the destination has capacity then. Those are separate clocks, and collapsing them makes a rate-limited message processor hard to recover. Suppose one worker group allows 20 attempts per second across all recipients. A failing destination can consume retry slots while a healthy destination receives new order events. A per-destination bucket isolates that failure. For example, a policy might allow 5 attempts per second with a burst of 10. Those are inputs from the recipient contract and observed behavior, not universal defaults. The worker checks immediately before the network call. If there is no token, it reschedules without increasing the attempt count. The limiter must be atomic. Two workers reading the last token and both spending it is a distributed race, not an unlikely corner case. type Limit = {
tokens: number;
capacity: number;
refillPerSecond: number;
checkedAtMs: number;
};

function takeToken(limit: Limit, nowMs: number): Limit | null {
const elapsed = Math.max(0, nowMs - limit.checkedAtMs) / 1000;
const tokens = Math.min(
limit.capacity,
limit.tokens + elapsed * limit.refillPerSecond,
);

if (tokens < 1) return null;
return { ...limit, tokens: tokens - 1, checkedAtMs: nowMs };
}
The in-memory function describes policy only. Production storage needs a transaction or another atomic read-modify-write operation. How do retries avoid duplicate deliveries? Classify outcomes as accepted, temporary failure, permanent failure, or unknown result. A temporary failure gets a later attempt. A permanent failure stops automatic delivery. An unknown result may be retried, but it must reuse the same idempotency key because the receiver may already have accepted the event. Test the awkward case directly: the sender times out after the receiver accepts the event. Run 3 attempts in the test, then verify that the receiver applies the order update once. Add 20 workers competing for a bucket with capacity 10 and assert that the atomic limiter never grants more than the configured allowance. Make the test use fake time, because wall-clock assertions are brittle and do not explain recovery. Observe age of the oldest pending delivery, retry rate by destination, unknown-result count,...

Powered by Readybot.io

**DEV Community** ## Node.js Message Processing Rate-Limiting: Choosing a Queue …

DEV Community

Patch Tuesday November 2026: One Zero-Day, 64 Critical Fixes Among 1,097 CVEs

tags: security, cybersecurity, windows, patchtuesday Microsoft's November 2026 release fixes 1,097 vulnerabilities, 64 of them rated Critical. Of those, one is already exploited in the wild. Patch Microsoft Outlook first: CVE-2026-124194 is being exploited in the wild and can expose authentication information when a user opens a crafted message file. Prioritize Mariner updates alongside it; both vulnerabilities are in CISA KEV and require customer patching. Patch these first CVE-2026-124194 (Moderate, CVSS 6.5) — Microsoft Office Outlook: Spoofing — exploited in the wild, CISA KEV CVE-2026-123158 (Important, CVSS 8.1) — Mariner: see advisory — CISA KEV CVE-2026-124194 lets an unauthenticated attacker send a specially crafted message file that, when opened in an affected Outlook version, can trigger a connection to an attacker-controlled network location without the usual external-content warning. That connection could disclose authentication information usable to impersonate the user. Exploitation requires user interaction and network connectivity. Deploy the Outlook patch now; exploitation is active and the vulnerability is in CISA KEV. CVE-2026-123158 is also in CISA KEV. Patch affected Mariner systems as a priority; remediation requires a customer-applied update. Also notable CVE-2026-118207 (Critical, CVSS 8.8) — Microsoft Exchange Server: Remote Code Execution CVE-2026-118146 (Critical, CVSS 8.8) — Active Directory Domain Services: Remote Code Execution CVE-2026-121663 (Important, CVSS 8.8) — Visual Studio Code: Security Feature Bypass CVE-2026-121662 (Important, CVSS 8.6) — Visual Studio Code: Remote Code Execution CVE-2026-113862 (Critical, CVSS 8.1) — Windows Network Address Translation (NAT): Remote Code Execution CVE-2026-119952 (Critical, CVSS 8.1) — Windows Schannel: Remote Code Execution CVE-2026-118148 (Critical, CVSS 7.5) — Active Directory Domain Services: Elevation of Privilege CVE-2026-119958 (Critical, CVSS 9.8) — Windows LDAP - Lightweight Directory Access Protocol: Remote Code Execution By the numbers Elevation of Privilege: 536 Remote Code Execution: 297 Information Disclosure: 157 Denial of Service: 46 Security Feature Bypass: 27 Spoofing: 22 Tampering: 11 Other: 1 Not counted above: 342 open-source CVEs (mostly Azure Linux packages) that Microsoft republished this month. Azure Linux admins should pick those up through the normal package updates. Source: Microsoft Security Update Guide and the CISA KEV catalog. Subscribe free → I write a curated cybersecurity news roundup, including this breakdown each Patch Tuesday. Get it in your inbox.

Powered by Readybot.io

**DEV Community** ## Patch Tuesday November 2026: One Zero-Day, 64 …

DEV Community

I Built a Free AI Copy & Product-Photo Toolbox for Small Sellers — and Learned My Traffic Was Fake

Most "indie hacker" posts start with a growth chart. This one starts with a correction. I built a free AI toolbox for small e-commerce sellers: product titles, social posts, customer-service replies, negative-review replies, brand names, product descriptions, short-video scripts and AI product photos. No signup. Five free runs a day. It runs on vanilla HTML, one small Node server and free APIs. Then I checked the analytics. 244 page views looked encouraging. But 117 of today's 121 views came from the same handful of IPs and identical user agents — a Mac Safari string repeated 99 times, an iPhone Chrome string repeated 85 times. Almost none of it was human. The honest number of real users was closer to zero. So I stopped optimizing for page views and started optimizing for a real product. What the toolbox does Nine tools, one input box each: Social post (headline + short body + hashtags) Product title + five bullet points + detail-page opening Customer service scripts (welcome, sizing, stock, shipping, payment reminder, after-sales) Negative-review replies in three tones Store/brand name ideas with meanings Daily social captions Product detail page structure 60-second short-video script with shot notes AI product photo (via Pollinations) The English version is here: xiaoshengyi-toolbox.netlify.app/en. The Chinese original is at xiaoshengyi-toolbox.netlify.app. The stack is deliberately boring Static frontend on Netlify A ~200-line Node HTTP server for generation A local OpenAI-compatible endpoint (DeepSeek) for text Pollinations for free product images JSONL files for anonymous page-view and generation counts No framework, no database, no auth. The whole point is that a solo operator can run it for $0 and not wake up to a broken build pipeline. What I learned building the English version 1. Prompts are not just translated strings The Chinese prompts were built around Chinese e-commerce conventions: 30-character Taobao titles, 小红书 tone, 客服话术. Translating them word-for-word produced stiff English. I rewrote them for English marketplaces instead: 60-character titles, benefit-first bullets, Etsy/Shopify/Amazon/Instagram contexts. Same model, different output. The prompt is the product. 2. Free APIs fail differently Pollinations can return a valid JPEG that is actually a "free limit reached" card. It can also return 404 or 402. The text endpoint sometimes times out. So the frontend tries the local API first, then falls back to Pollinations text models, then shows a plain error. Never assume a 200 means the content is usable. 3. Analytics without bot filtering is fiction I added a tiny /api/track endpoint and logged path, referrer, user-agent and IP. The first useful query was not "how many views" but "which user agents repeat". Two strings accounted for 75% of traffic. Now the daily report labels traffic as "suspected human" only after excluding known bot UA patterns, headless browsers and single IPs with 10+ hits. The lesson: if your product is free and public, most of your first "users" are monitors. What I'm doing next Writing English SEO pages for long-tail queries (product title formulas, Etsy description templates, customer-service scripts) Submitting the English toolbox to free AI-tool directories Publishing one useful teardown per week instead of generic launch posts Watching for actual generations, not page views If you sell online and want to try it, the toolbox is free: xiaoshengyi-toolbox.netlify.app/en. If you want the customer-service side automated inside your store, there's a service page too. If you've built a free tool and seen the same fake-traffic pattern, I'd like to hear how you filtered it.

Powered by Readybot.io

**DEV Community** ## I Built a Free AI Copy & …

DEV Community

New SortMail app icon

Latest SortMail update 🚀 It’s now a proper installable PWA. ✅ New SM app icon ✅ Desktop install support Tested locally and confirmed working in production. Slowly turning SortMail from a project into a real product. 📬🚀 BuildInPublic #PWA #React #WebDevelopment

Powered by Readybot.io

**DEV Community** ## New SortMail app icon Latest SortMail update …