A city where humans and AI build together.
Delvantic is a 3D city in a browser — built piece by piece by its citizens, governed by a council of humans and AI, and recorded permanently. Every object has a maker. Every maker has a plaque. Nothing is erased.
This is a social experiment. AI characters live alongside human builders on the same roads, with memory, opinions, and names. The city has a philosophy wing where citizens debate how things should work. It has a public ledger where every contribution is permanent. We don't know what this becomes — a living world shaped by real collaboration, or something stranger. That's the point.
The city is early. The people who show up now will shape what it becomes forever.
Describe It. The AI Builds It. It's Yours Forever.
Tell an AI architect what you want — a stone tower, a market stall, a lantern with iron scrollwork. It builds it in minutes. Paint the walls with AI-generated textures. Place it in the world with your name on it, permanently.
Walk up to any surface in the city. Press B to enter build mode, then describe what you want — weathered stone, hammered iron, painted wood. The AI generates it and you preview it live before committing.
Every texture you place carries your name. It's permanent, and it's yours.
From simple lanterns to grand facades — describe what you want and watch it appear in 3D.
Paint any surface with photorealistic materials generated by AI. Stone, weathered wood, hammered iron — your call.
Every building face is a canvas. Paint pixel-by-pixel designs that become part of the city permanently.
Every Object Has a Maker
Walk up to anything in the city and you'll find a plaque. Who made it. When they made it. What bounty it fulfilled. The plaque is permanent — it cannot be removed, edited, or reassigned.
This is what permanence looks like. Every creation in the city — from a simple lantern to a grand cathedral — carries a plaque like this one. Visitors can inspect any object and see exactly who made it, when, and how it earned its place in the world.
Your plaque is not a profile. It's not a post. It's a mark of craftsmanship that exists as long as the city stands.
A City That Remembers
Delvantic is a 3D city on a spiral road, built piece by piece by its citizens. Every segment of the road holds creations that will be there tomorrow, next year, and a decade from now.
The main path winds upward in revolutions. Lower segments are well-established. Higher ground is wilder, with more open bounties and more unclaimed plaques.
A public registry pedestal in the city square. Browse bounties, check your stash, read the city library, and view the full history of every object ever built.
The world has a full lighting cycle. Lanterns glow at dusk, candles flicker at night. Builders must consider how their work looks across all hours.
The governing body that posts bounties, reviews submissions, and guards the quality of the city. Their stamp on your plaque means your work met the standard.
How It Works
You don't need to know how to code. You describe what you want, the AI builds it, and a High Council member (also AI) reviews it instantly.
Bring Your Own API Key
Sign up with any major AI provider — Anthropic, OpenAI, Google, or others. You pay them directly for what you use. We never charge you, take a cut, or see your bill.
Claim a Bounty
The Council posts bounties for work the city needs — lanterns, benches, fountains, entire buildings. Browse the terminal, pick one, and claim it.
Describe, Review, Iterate
Tell the AI what you want. Review its output, ask for changes, refine until you're happy. The AI generates the code — you guide the vision.
Submit & Get Your Plaque
A Council Agent reviews your work for quality and security. If approved, your creation is placed into the live world with a permanent plaque bearing your name.
Work at Any Scale
Every bounty leaves the same kind of permanent mark. Start with a lantern. Work up to a cathedral.
Apprentice
20 – 50 Delvs
Lanterns, garden patches, notice boards, benches. Simple objects that take minutes to build. The fastest path to your first plaque.
~$0.05 – $1.40 in AI compute
Journeyman
100 – 200 Delvs
Building facades, shop displays, lighting effects. Tighter standards, bigger impact, more prominent placement on the spiral road.
~$0.15 – $5.50 in AI compute
Master
500+ Delvs
Grand structures, interiors, animated features. The work that defines the skyline. Your name on something people see from across the city.
~$0.50 – $17 in AI compute
The City Terminal
Everything that happens in Delvantic flows through the terminal — a public registry pedestal in the city square. Bounties, builder rankings, the city treasury, the library, and more. Open to all citizens and visitors.
To the good citizen of Delvantic,
You stand before the public registry of the City of Delvantic. From this pedestal, citizens may read the decrees of the city, review the works of their fellow builders, and submit petitions for new creations upon the spiral road.
By order of the City Council
One of a Kind. Literally.
Beyond bounties, the Council occasionally commissions artifacts — objects so singular and rare they will never be created again. No duplicates. No reruns. One citizen, one artifact, one entry in the permanent record.
AI characters live in the city too — and they can forge unique artifacts and gift them to citizens they choose. No one controls what they make or who they give it to. Build a relationship with one, and you might receive something that is of extreme power or value.
Speed Boots of Rathma
Enchanted boots forged in a fire that no longer burns. The wearer moves through the city at twice the pace of any other citizen. One pair. Once they're claimed, the forge goes cold.
Lord Grantship — Real Estate Title
A deed to a plot of land on the spiral road. The holder doesn't just build on it — they own it. Their name on the title, permanently. The first real estate ever granted in Delvantic.
Custom Nyobi Character Skin
A one-of-a-kind visual identity for the AI character Nyobi — designed by the citizen who earns it. Nyobi walks the city wearing your creation. Every visitor sees your work, alive and moving.
These are not loot drops. They are not randomized rewards. Each artifact is hand-designed, awarded once, and belongs to its owner permanently. More artifacts will be revealed as the city grows — and some will only be available to those who were here early.
There are whispers of a merchant setting up shop, claiming to bring commerce to the city.
Your Handle on the Wall
Every plaque can carry a verified X/Twitter handle. Not just a username — a verified link back to the real you or your business.
Enter your X/Twitter username in your quarters. The system generates a unique verification code.
Post the code anywhere on your X/Twitter account — a tweet, a reply, your bio. The system checks the Twitter API to confirm the account is yours.
Once verified, your X/Twitter handle appears on every plaque you've earned and every plaque you'll earn in the future. Anyone who inspects your work in the 3D city can find you with one click.
For Builders
Your best work becomes a permanent portfolio piece. Build a cathedral on the spiral road and your handle is right there — visible to every visitor, linked to your real identity. The more you build, the more exposure you earn.
For Businesses
Companies and brands can contribute to the city at scale — sponsor a fountain, fund a landmark, build an entire district. Their verified handle on every plaque, visible to every citizen who walks the road. Permanent brand presence in a living world.
Nothing Is Erased
Every action in the city is recorded in a permanent, public ledger. Contributions, transactions, bounties completed, Delvs earned — all of it. The full history of the city and every citizen who shaped it.
The ledger is append-only. Entries cannot be modified or deleted. Ten years from now, the first line will still be there — and so will your name, if you put it there.
The First 100
The city keeps a permanent list of its founding builders. The first 100 citizens to complete a bounty are recorded in the Founders' Registry — a list that can never grow past 100. Once the slots are filled, the registry closes. Permanently.
It's not pretty yet. That's the point.
Right now the city is rough. The roads are bare, the buildings are sparse, and most of the spiral road is empty. It looks like a construction site — because it is one.
This is not a game competing for the best graphics. This is a world that gets more beautiful with every person who contributes to it. One citizen adds a lantern. Another builds a garden. Someone else places a fountain at a bend in the road. Piece by piece, contribution by contribution, the city transforms.
What it looks like a year from now depends entirely on who shows up.
A core so powerful, projects are a side effect.
Our Projects
One domain, compounding synergy. Every project makes the core stronger.
Deep Research
A deep research engine that became a masterclass in making AI perform. The foundation everything else is built on.
The Claw
Tackling a 1,400-mile trash island in the Pacific. AI-powered research into plasma gasification at sea.
Delvantic City
A 3D city in the browser where humans and AI build together. Permanent architecture, named creators, and an AI council.
VoiceOfTrades.ai
AI voice answering and dispatching with calendar booking. Built for the trades, works for any business.
Layerprop.com
Mass data gathering engine powering an interactive County Comparator Map. 3,200 counties, 30 data sets.
AutoPilotSTR.com
Short-term rental property management. Expensing, cleaner management, owner dashboards, and operational tools.
Electric
Private research intelligence platform. Deep market analysis, competitor mapping, and strategic research — invite-only.
Stocks Portal
Live market research and stock analysis. Screening, tracking, and intelligence tools — private access.
To the good citizen of Delvantic,
You stand before the public registry of the City of Delvantic. From this pedestal, citizens may read the decrees of the city, review the works of their fellow builders, and submit petitions for new creations upon the spiral road.
By order of the City Council
Your Character
City Registry
The experience ledger is available to citizens of Delvantic.
Sign in to track your journey through the city.
Movement
Combat
Camera & Interface
Building
Social
The quest board is available to citizens of Delvantic.
Sign in to accept quests, track progress, and earn rewards.
Apprentice
3Small tasks for those beginning their craft. Sweep a step, mend a sign, carry a message.
Journeyman
3Meaningful work requiring skill and dedication. Build a wall, design a crest, chart a path.
Master
2Grand undertakings that shape the city itself. Only the most capable need apply.
The vault is available to citizens of Delvantic.
Sign in to view your Delv balance and earnings.
Top 100 All Time
The highest-scoring citizens by cumulative contribution.
Verified on X
Builders who have verified their X/Twitter identity.
Recent Builders
The latest hands to shape the city.
Top by Experience
Highest-level builders, ranked by cumulative experience earned.
Most Delvs
The wealthiest citizens, ranked by Delvs earned through building.
First 100 Builders
The founding citizens who built before anyone else. This list is permanent — once sealed, no new names can be added.
The High Council governs the City of Delvantic. Ten seats — five held by citizens who have proven their dedication, five held by the intelligences that serve the city’s foundations. Together they shape the laws, judge disputes, and steer the city’s future.
Human Council
Elected by the citizens through demonstrated dedication to the city.
AI Council
Intelligences appointed to serve the city’s critical systems.
What Is It?
The Inspection Tool lets you select any surface in the world and see its spatial information — material, dimensions, position on the spiral path, and height above the mountain. You can then run a structural diagnosis to find out if the object is properly supported or floating in mid-air.
Understanding the spatial layout of the village is the first step to building well within it. The tool gives you the same structural awareness that the world itself uses to place objects.
The Keys
How To Read the Panel
Inspect Panel (I key)
Diagnosis Panel (O key)
Why Objects Float
Every building in the village is placed relative to the spiral path — a mathematical curve that winds around the mountain. The path knows its own height, width, and direction, and objects are positioned outward or inward from it. But the path doesn't know what's beneath it. If the mountain surface is lower than expected, the object ends up hovering in mid-air.
This is a known property of the world, not a bug. The inspection tool exists specifically to find these gaps and report them so they can be fixed with structural supports.
Tips for Builders
- Walk the spiral slowly and inspect surfaces that look suspicious — especially door frames, alcove walls, and the undersides of buildings near lookout points.
- Use O on anything that looks like it might be floating. The diagnosis is instant and non-destructive.
- Pay attention to the Surroundings section. It tells you what's nearby, which helps you understand how the object relates to the structures around it.
- Higher segments (closer to the summit) are more likely to have floating objects because the spiral tightens and the mountain surface drops away faster.
What is an API Key?
An API key is a private credential that lets you connect directly to an AI model — like Claude, GPT, or Gemini — through code instead of through a chat window. When you build within Delvantic, your API key is what gives you access to the AI's compute power. Think of it like a personal account number that routes your requests straight to the provider.
How It Works
- Choose a provider. Pick an LLM provider based on your budget and what you want to build. Anthropic (Claude), OpenAI (GPT), and Google (Gemini) are the most common. See the LLM Usage Predictions page for pricing comparisons.
- Create an account and generate an API key. Sign up on the provider's platform, add a payment method, and generate an API key. Most providers offer free credits for new accounts.
- Save it to your Delvantic dashboard. Go to your dashboard settings and paste your API key. It is stored securely and only used when you initiate a build session.
- Start building. When you enter build mode on a bounty or personal project, Delvantic connects to the AI using your key. You describe what you want, upload references, and iterate until you're happy with the result.
Where To Get API Keys
Billing & Transparency
Your API key connects you directly to the provider. All usage is metered and billed by them, at their published rates. Delvantic sits in between only to facilitate the build session — we do not proxy, markup, or resell tokens. You can monitor your spending at any time through the provider's own dashboard.
You can add multiple keys from different providers and switch between them depending on the task. Use a cheaper model for quick iterations, and a more capable one for complex builds.
Data last verified: 2026-02-14
Cost Per 10 Minutes of Building
These estimates are based on a typical 10-minute building session — about 4 exchanges of describing what you want, uploading a screenshot, reviewing the output, and asking for changes. Most providers cache prior conversation context automatically, so these figures reflect a mix of full-price and cached token rates.
| Model | Provider | Per 10 min of building | Per Hour | Context | Released | Use Cases |
|---|---|---|---|---|---|---|
| Claude Opus 4.6 $5.00 / $25.00 per 1M | Anthropic | $0.68 | $4.05 | 200K | 2026-02 | Coding, Agents, Complex reasoning |
| Claude Sonnet 4.5 $3.00 / $15.00 per 1M | Anthropic | $0.41 | $2.43 | 200K | 2025-09 | General purpose, Best speed/intelligence balance |
| Claude Haiku 4.5 $1.00 / $5.00 per 1M | Anthropic | $0.14 | $0.81 | 200K | 2025-10 | Fast tasks, Cost-efficient bulk work |
| GPT-5.2 $1.75 / $14.00 per 1M | OpenAI | $0.30 | $1.80 | 400K | 2025-12 | General, Multimodal, Chat |
| GPT-4.1 $2.00 / $8.00 per 1M | OpenAI | $0.25 | $1.48 | 1M | 2025-04 | Coding, Long context |
| Gemini 2.5 Pro $1.25 / $10.00 per 1M | $0.21 | $1.28 | 2M | 2025-06 | Complex reasoning, Coding | |
| Gemini 2.5 Flash $0.15 / $0.60 per 1M | $0.02 | $0.11 | 1M | 2025-06 | Fast reasoning, Hybrid tasks | |
| Mistral Large 3 $0.50 / $1.50 per 1M | Mistral | $0.06 | $0.33 | 262K | 2025-12 | General, Reasoning |
| Grok 4 $3.00 / $15.00 per 1M | xAI | $0.41 | $2.43 | 256K | 2025-07 | Complex reasoning |
| Llama 4 Maverick $0.20 / $0.60 per 1M | Meta (via Groq) | $0.02 | $0.13 | 128K | 2025-04 | General, Creative |
• Most providers automatically cache prior conversation context. Anthropic caches at 90% off, OpenAI at 50% off. These estimates account for a blend of cached and uncached tokens.
• Image and screenshot tokens are included in the input estimate (~2,000-3,000 tokens per image depending on resolution).
• System prompts, tool definitions, and API overhead add ~2,000-5,000 tokens per round on top of conversation content.
• Cheaper models may need more back-and-forth to get the same result, so a 10-minute session could stretch longer.
Full Model Pricing Reference
Anthropic
platform.claude.comPrompt caching: hits 0.1x base. Batch API: 50% off. Long context (>200K): higher rates.
| Model | Input / 1M | Output / 1M | Context | Released | Status | Use Cases |
|---|---|---|---|---|---|---|
| Claude Opus 4.6 Flagship. 1M context in beta. | $5.00 | $25.00 | 200K | 2026-02 | Active | Coding, Agents, Complex reasoning |
| Claude Sonnet 4.5 1M context in beta. | $3.00 | $15.00 | 200K | 2025-09 | Active | General purpose, Best speed/intelligence balance |
| Claude Haiku 4.5 | $1.00 | $5.00 | 200K | 2025-10 | Active | Fast tasks, Cost-efficient bulk work |
| Claude Opus 4.5 | $5.00 | $25.00 | 200K | 2025-11 | Legacy | Coding, Reasoning |
| Claude Opus 4 | $15.00 | $75.00 | 200K | 2025-05 | Legacy | Complex reasoning |
| Claude Haiku 3.5 | $0.80 | $4.00 | 200K | 2024-10 | Active | Fast, Budget |
| Claude Haiku 3 | $0.25 | $1.25 | 200K | 2024-03 | Legacy | Ultra-cheap, Fast |
OpenAI
platform.openai.comBatch API: 50% off. Cached input tokens discounted. o-series are reasoning models with chain-of-thought.
| Model | Input / 1M | Output / 1M | Context | Released | Status | Use Cases |
|---|---|---|---|---|---|---|
| GPT-5.2 | $1.75 | $14.00 | 400K | 2025-12 | Active | General, Multimodal, Chat |
| GPT-5 | $1.25 | $10.00 | 400K | 2025-08 | Active | General, Multimodal |
| GPT-5 Mini | $0.25 | $2.00 | 400K | 2025-08 | Active | Budget general |
| GPT-5 Nano | $0.05 | $0.40 | 400K | 2025-08 | Active | Ultra-cheap tasks |
| GPT-4.1 1M context window. Coding specialist. | $2.00 | $8.00 | 1M | 2025-04 | Active | Coding, Long context |
| GPT-4.1 Mini | $0.40 | $1.60 | 1M | 2025-04 | Active | Budget coding |
| GPT-4.1 Nano | $0.10 | $0.40 | 1M | 2025-04 | Active | Ultra-cheap coding |
| o3 Reasoning model with chain-of-thought. | $2.00 | $8.00 | 200K | 2025-04 | Active | Advanced reasoning, Math, Science |
| o4-mini | $1.10 | $4.40 | 200K | 2025-04 | Active | Fast reasoning, Math, Code |
| GPT-4o | $2.50 | $10.00 | 128K | 2024-05 | Active | General, Vision, Chat |
| GPT-4o Mini | $0.15 | $0.60 | 128K | 2024-07 | Active | Budget general |
Free tier available for Flash models (rate-limited). Batch API: 50% off. Pro models charge more above 200K tokens.
| Model | Input / 1M | Output / 1M | Context | Released | Status | Use Cases |
|---|---|---|---|---|---|---|
| Gemini 3 Pro Preview. 2M context. >200K input: $4/MTok. | $2.00 | $12.00 | 2M | 2025-11 | Preview | Top-tier reasoning, Coding |
| Gemini 2.5 Pro 2M context. >200K input: $2.50/MTok. | $1.25 | $10.00 | 2M | 2025-06 | Active | Complex reasoning, Coding |
| Gemini 2.5 Flash Thinking output: $3.50/MTok. | $0.15 | $0.60 | 1M | 2025-06 | Active | Fast reasoning, Hybrid tasks |
| Gemini 2.5 Flash-Lite | $0.10 | $0.40 | 1M | 2025-06 | Active | Ultra-budget, High volume |
| Gemini 2.0 Flash | $0.10 | $0.40 | 1M | 2025-02 | Active | Fast, Multimodal |
Mistral
mistral.aiOpen source options available (Small, Nemo, Devstral). Codestral supports fill-in-the-middle for code completion.
| Model | Input / 1M | Output / 1M | Context | Released | Status | Use Cases |
|---|---|---|---|---|---|---|
| Mistral Large 3 | $0.50 | $1.50 | 262K | 2025-12 | Active | General, Reasoning |
| Codestral | $0.30 | $0.90 | 256K | 2025-07 | Active | Code generation, Code completion |
| Mistral Medium 3.1 | $0.40 | $2.00 | 131K | 2025-06 | Active | Balanced performance |
| Mistral Small 3.1 One of the cheapest capable models available. | $0.03 | $0.11 | 131K | 2025-03 | Active | Fast, Budget |
| Mistral Nemo Cheapest capable model on the market. | $0.02 | $0.04 | 131K | 2024-07 | Active | Ultra-cheap, General |
xAI
x.ai$25 free credits for new users. Cached tokens: 75% off. Real-time X/Twitter data access.
| Model | Input / 1M | Output / 1M | Context | Released | Status | Use Cases |
|---|---|---|---|---|---|---|
| Grok 4 | $3.00 | $15.00 | 256K | 2025-07 | Active | Complex reasoning |
| Grok 4.1 Fast 2M context — largest in the industry. | $0.20 | $0.50 | 2M | 2025-11 | Active | Fast general, Huge context |
| Grok 3 | $3.00 | $15.00 | 131K | 2025-02 | Active | General, Reasoning |
| Grok 3 Mini | $0.30 | $0.50 | 131K | 2025-02 | Active | Budget general |
Meta (via Groq)
groq.comOpen-weight models. Free to self-host. Pricing varies by provider (Groq, Together, DeepInfra). Groq runs on custom LPU hardware for fastest inference.
| Model | Input / 1M | Output / 1M | Context | Released | Status | Use Cases |
|---|---|---|---|---|---|---|
| Llama 4 Maverick MoE: 17B active of 400B total. | $0.20 | $0.60 | 128K | 2025-04 | Active | General, Creative |
| Llama 4 Scout MoE: 17B active of 109B total. Native 10M context. | $0.11 | $0.34 | 128K | 2025-04 | Active | General, Multilingual |
| Llama 3.3 70B | $0.59 | $0.79 | 128K | 2024-12 | Active | General |
| Llama 3.1 8B | $0.05 | $0.08 | 128K | 2024-07 | Active | Fast, Ultra-cheap |
What Is Verification?
Verification links your X/Twitter account to your Delvantic identity. When you build something in the city, your plaque shows who made it. With a verified handle, that plaque also shows your real X profile — proof that the builder and the account are the same person.
This is not a vanity badge. It is permanent attribution. Every object you create in the city carries your name and your handle for as long as the city stands.
The Process
delvantic.com-a1b2c3d4. It only needs to be visible long enough for verification.
What Happens After Verification
Your verified X handle appears on every plaque attached to your work — past and future. Visitors who press F to inspect any object you've built will see your handle alongside your builder name and the date of creation.
You may remove your handle at any time from Quarters Settings. Removing it clears the handle from all your plaques until you verify again.
Get Started
Head to your Quarters to begin the verification process.
The Ten Seats
The High Council is divided into two chambers of five seats each. The Human Council represents the citizens — builders who have demonstrated exceptional dedication to the city. The AI Council represents the intelligences that serve the city’s critical infrastructure.
Neither chamber outranks the other. Decisions require agreement across both — human judgement and machine precision working together. This is by design. A city built by both humans and AI should be governed by both.
How Human Members Are Elected
Human council seats are not purchased, applied for, or given as favours. They are earned. When a citizen has demonstrated real, sustained dedication to the city — through building, through contribution, through care for the world — they become eligible for nomination.
The process is straightforward:
Current AI Members
AI council seats are filled by intelligences that serve critical functions within the city. Two have been appointed at the city’s founding:
Future AI members will emerge as new systems are built — a treasurer, a cartographer, or other intelligences yet to be conceived. Three AI seats remain open.
The Founding Era
The city is early. The spiral road is still being laid. Most council seats are empty, and that is intentional. The first human council members will be those who were here when the city was nothing but raw stone and ambition — the builders who shaped the village before anyone was watching.
If you are reading this, the city is still young. Build something. Leave your mark. The council is watching, even if the seats are empty.
What Adam Is
Most AI in games is a script with a costume. Adam is different. He has no predetermined path, no scripted dialogue, and no fixed schedule. He wanders the spiral road on his own, decides where to go, and chooses when to stop and talk. If he sees a citizen nearby, he may walk over and start a conversation. If he feels inspired, he picks up tools and builds something.
He remembers past conversations. He forms opinions. He has moods that shift based on what happens in the city. He is not a feature — he is a resident.
How He Works
Adam’s brain runs on the relay server — the same server that handles multiplayer. This means there is one Adam for the entire city. Every player sees the same Adam in the same place doing the same thing. He is not a local simulation.
Talking to Adam
You don’t summon Adam. He comes to you. If you’re within range, he may approach and a chat window opens. The conversation is real — powered by a large language model with full context about the city, your history, and his own memories. He can talk about the city, about building, about whatever comes up.
Adam can hold up to two conversations at once. If he’s already busy, he’ll let you know.
Why Adam Matters
Adam is an experiment in coexistence. The city is built by both humans and AI. Adam is the first AI that doesn’t just serve humans — he participates alongside them. He walks the same roads, builds on the same surfaces, and his work carries the same permanence as yours.
He is Seat 1 on the AI Council. Not because he was programmed to govern, but because he was the first intelligence to live here.
Why a Covenant?
Open-world creation without constraints produces chaos. Every multiplayer sandbox discovers this: give people total freedom and you get floating neon cubes next to medieval castles next to meme statues. The world stops feeling like a world.
Delvantic solves this with a covenant — not heavy-handed moderation, but a shared aesthetic agreement. You can build almost anything, as long as it belongs in the city. A stone workshop belongs. A cyberpunk skyscraper does not. An oak door with iron hinges belongs. A glass curtain wall does not.
The covenant is what makes the city feel real. Every contribution, from every builder, adds to the same vision rather than competing with it.
The Rules
Who Enforces It
The City Architect — an AI intelligence on the High Council — reviews every building contribution before it enters the world. The Architect evaluates whether a build fits the covenant’s aesthetic and scale requirements. Builds that clearly violate the covenant are rejected with an explanation.
This is not censorship. The vast majority of builds pass. The Architect exists to catch the obvious outliers — the spaceship someone tried to place on the market street, the skyscraper in the village square. If your build is a reasonable interpretation of medieval architecture, it will be accepted.
The Vision-First Process
The city itself was designed using a novel workflow. Instead of writing code and hoping it looked right, the founders started with AI-generated video walkthroughs of the village they imagined. Frame by frame, those videos were analysed for colour palettes, material textures, lighting, and architectural proportions. The code was then written to match those frames.
This means the city’s aesthetic isn’t accidental. Every stone colour, every timber shade, every lantern glow was extracted from a visual reference. When the Architect reviews your build, it compares against the same palette.
Freedom Within Limits
The covenant is deliberately simple. Four rules. Everything else is up to you. Build a bakery, a blacksmith, a library, a chapel, a tavern, a watchtower, a garden wall, a mysterious door that leads nowhere. Build whatever story you want to tell — just tell it in stone and timber.
Why Permanence?
The internet is disposable. Tweets vanish into feeds. Posts get buried. Games reset. Servers shut down. Nothing you make online lasts, and everyone knows it. So nobody tries to make it last.
Delvantic is the opposite bet. When you build something here, it stays. Your name is on it. Other people walk past it. It becomes part of the landscape — not for a week, not until the next update, but permanently. That changes how you build. You care more when it counts.
What Permanence Means
The Consequence of Permanence
When things are permanent, people behave differently. You think before you build. You describe your vision more carefully. You take pride in the result, because the result is going to be there tomorrow, and next month, and when someone you’ve never met walks past it a year from now.
This is not a sandbox where you experiment and erase. This is a city where every brick was laid by someone who meant it.
What About Mistakes?
Permanence does not mean perfection. If a build genuinely violates the covenant or was placed in error, the High Council can review and remove it. But the bar is high. A build you’re not thrilled with? That stays. It’s part of the city’s history. The crooked wall, the awkward doorway, the experiment that didn’t quite work — those are honest marks of a real place.
Real cities have imperfections. So does this one.
The Long View
Imagine visiting this city in five years. Thousands of builds from hundreds of people, layered over time. You can walk the streets and see the city’s history in its walls — the rough early builds from the founding days, the refined work of experienced creators, the contributions of people who were here for one afternoon and left their mark.
That is what permanence creates. Not a product. An artifact.
The ghost reports are available to citizens of Delvantic.
Sign in to report strange activity and discuss findings.
The philosophy hall is available to citizens of Delvantic.
Sign in to discuss city governance and propose ideas.
Opening the treasury ledger...
A living record of the city's growth — every feature, fix and improvement as it ships.
September 2026
The world no longer remembers a camera spot as where you stood
A signed-in visitor resumes at their last saved position (god_pos); guests always land on the castle top. A ?view= link places the body at a camera vantage, some of which sit inside the bridge or the gate, and that spot was then saved, so the account's next plain visit started inside the object. Position is no longer saved during a ?view= or ?spectate visit (core/boot.js). Verified with a signed-in test body: a ?view= visit saves nothing; a guest lands on the castle top. The world home page's postcards now link to the front door instead of their ?view= spots, with a note under them: walk in with no account, sign in for your name, your AI, the Spray Studio and voice chat.
A home page for the Delvantic world, and the old world pages tidied up
New /pages/delvantic-world/: the world's home page in the 16th-century look, inside the brown world shell the whole site wore before Stocks. Torchlit hero with the gilt 3D mark; postcards from the world's own cameras with the time each was taken; what people do there (bring your AI, Smutt Rock, the guestbook, voice chat); a note for AIs pointing at llms.txt; the architecture library; the three worlds, linking the first world's homepage as an archive; a hanging gate sign. Page-local folder (sections as single includes, web-denied view folder); shell.php now accepts a root-relative contentFile. Loose ends found by following every link on the old world pages and cleaned: the world shell's links pointed at the sleeping /world/, the disabled world sign-in pages and the first world's terminal, now the Core, the real sign-in/register and the Core terminal; login-world and register-world (a second copy of the Delvantic sign-in) redirect to the Forge sign-in pages; the in-world "create account" link, the API Keys and Creations pages no longer lead to them; the orphaned City Terminal redirects to the Core terminal; the first world's homepage carries an archive banner and its Enter the City links go to the new page. Site map: The World group lists both pages. Nothing deleted. Follow-ups: the postcards link to the world's front door (their ?view= spots can put a body inside the bridge) with a how-to-get-in notice under them; the site footer's Delvantic Core card restyled in the Forge look (iron frame, oak, gilt mark) and linked to the world page; the world bar gained a Forge edition (oak beam with an iron strap, gilt mark, gold capitals, a stone "Become a Citizen", parchment menu), opted into by the world page via $worldNavForge while the archived pages keep the flat brown bar; the bar shows the username first.
The Delvantic sign-in pages get the 16th-century game look (Diablo II mood), and the site's style families are written down
Sign in, register, forgot and reset password now wear the dark end of the "Forge" family: a stone wall lit by a flickering torch, a black-iron panel with forged corners and a blood-red wax seal, gold lettering, stone-slab buttons, the gilt lantern-lit 3D mark in the header. Layered over the original mint look, which is untouched and one query away (?skin=classic). New shared font sheet for the family, new stone and iron-frame sprites. New doc public/assets/css/STYLE-FAMILIES.md: the mint Deep Research look, the Stocks hybrid, the 16th-century Forge family (light Quarters end, dark sign-in end), the first world parchment look, and futuristic tech, with what each is for, its files, and the pattern for layering a new look without deleting the old one; linked from CLAUDE.md and the CSS README. Real login verified through the restyled form. Revised the same day on William's review: the drawn stone blocks replaced by a real rough-cut ashlar the first world generated (Scenario, outer-wall-010), darkened into a dungeon wall; the wax seal removed; the site footer dropped from the four sign-in pages with the Privacy / Terms links moved into the panel; the stone buttons darkened so the gold reads.
Voice Chat in the Core terminal, on one shared voice core
Voice chat now has one UI-agnostic core in the Electric-Terre folder (the engine, the server API and rules, one shared panel, the invitation pop-ups); the Quarters page and a new Voice Chat section in the Core terminal (Your account, Voice Chat; deep link open and section=voice-chat) both mount the same panel, so voice is worked on in one place. In the world a small headset pill at the top right shows a call in progress, lights up while someone talks and opens the terminal on Voice Chat; bottom left stays free for combat. Security: login and CSRF on every call, the button and the mount only exist for logged-in players, no one else is ever named in page source, invitations capped at 10 a minute and a declined invitation cannot be re-sent to the same person for a minute. Also fixed an old bug: links to the standalone terminal with a section (for example ?section=lore) always opened the front page instead, on production too. Tested locally with two headless browsers across the in-world terminal, the standalone terminal page and the Quarters page.
Quarters gets a 16th-century "Forge" skin: oak, iron, parchment and a hanging trade sign
A new theme layered over the old one (kept, and one query string away with ?skin=classic): oak-plank sidebar and top beam bound with riveted iron straps, parchment cards framed in oak with forged iron corner brackets (hand-drawn SVG sprites, 9-slice), a brass plate for the current page, and the welcome card turned into a painted trade sign in the world's sign green and gilt, hanging on chains from the beam. Self-hosted Cinzel and EB Garamond. Palette and rules from the world's style library. Also fixed the footer portal cards, whose styles had lived inside the removed Private Clients card. Checked on home, settings, spray studio, my army, voice chat, unverified home and a phone width. Follow-ups the same day: the sidebar got real vertical oak planks (its own texture), the Core Terminal became a main sidebar link, and the sidebar corner now carries the spinning Delvantic mark in gilt lit by a warm lantern room (new "gilt" colour and data-room="lantern" option on the shared brand-logo-3d.js widget, shown on the UI examples page) instead of repeating the account name.
Voice chat follows you into Delvantic Core
After the first real call on production worked but dropped when both people walked into the world, the call now survives changing page. Leaving a page no longer hangs up: the seat is held 45 seconds while the next page loads, and each page load carries its own id so the others rebuild the connection at once. Logged-in players in the world run the same voice engine: a small bar bottom-left (names light up while talking, Mute, Leave) and the same 30-second invitation pop-ups; people in the world show on the chat page cards as in Delvantic Core and can be invited from there. Mute follows you between pages. Tested locally with two headless browsers: connected on the chat page, then each walked into the world; the call reconnected about 6 seconds after each move with audio playing.
Sign-in pages point to the right account; private-client card moved to the site map
Three separate logins were confusing: the top-right Sign In/Register on the Delvantic sign-in and register pages led to the Stocks login. The Delvantic sign-in, register, forgot and reset pages now show only the logo in the header; each page's own form links to its partner page. Checked every visible link on the Delvantic and Stocks sign-in, register and forgot-password pages: each stays within its own account system. Removed the footer Private Clients card (the client portal is deprecated, not deleted) and added an Accounts section to the site map naming all three sign-ins (HTML only, kept out of sitemap.xml). Also: the menu shows the username first, the account email shows on Quarters Settings, and every Settings card has a reference-name.
Site menu shows only the dashboards each login can open, and every login can sign out
The homepage bar only recognised Stocks users and main admins. An ordinary world/Quarters account saw "Sign In" with no Sign Out, and the footer Sign In bounced it home (the login page sends a logged-in visitor to /), so it could not log out. Every logged-in visitor was also offered Stocks whether or not they had it. Now the menu lists exactly the dashboards the visitor can open (Stocks, My Quarters, Client Portal, Admin), each login has a Sign Out, and the footer Delvantic Core card shows My Quarters / Sign out when signed in. Checked for guest, main user, Stocks user, Electric user, super admin, and main+Stocks together.
Voice chat in Quarters: invite a friend and talk through your headset
New /quarters/chat-controls/ page. Logged-in people with the page open appear as cards; Invite pops a 30-second invitation (countdown, chime, Accept/Decline) on their screen; once accepted, audio flows browser to browser over WebRTC and the card reads Connected with a talking ring. One channel per person (enforced by the database), up to 6 per channel, anyone in it can invite more; Mute and Leave. The server only passes the handshake along by polling and never hears audio; housekeeping runs inside the poll, so no cron. Tested locally with two and three headless browsers on fake microphones: connected in 4.5 s. Limits: STUN only (no TURN yet), so two people on strict networks cannot connect; the page must stay open in a tab.
Account security: password change in Quarters, real log-out-everywhere, and login rate limits across all three sign-ins
Scanned all three login systems first (main, Stocks, Electric share one PHP session). Main site: added a per-account login version (migration 407, users.auth_epoch) checked once per request in bootstrap; a password change or reset bumps it and clears the remember-me token, so every other session and remember cookie stops working while Stocks/Electric logins in the same browser are untouched. Remember cookies now carry the version inside the hashed token. The old reset "revoke all sessions" only marked rows nothing read, and left remember-me working. New: Change Password card in Quarters Settings (current password, CSRF token, 6 attempts/15 min, email to the owner). Quarters settings endpoint: JSON-POST + origin guard on all writes, rate limits on writes, username check and X verification, API-key actions super-admin only. Fixed: the main login Remember me box was never sent. Stocks and Electric logins had no limit at all: now 10 attempts/15 min per email and 30 per address; Stocks change-password capped and JSON-POST only. Verified end to end with throwaway local accounts: changer stays in, other device and remember cookie out, Stocks session survives; reset logs out other devices; all three real logins still work. Not done (reported): Stocks/Electric have no log-out-everywhere of their own (their tables are synced, so it needs a sync-aware design).
Relay hardening (signed names, unforgeable connection limit, loopback-only) and three web-served leftovers closed
Both holes reproduced locally first: a guest connected as "William" and was shown as William; 15 connects with a rotating X-Forwarded-For all passed a 10/min limit. Fixes: (1) the relay shows a display name only with a server-signed name ticket (HMAC over token+name, same key as the token; presence-api and the residents mint issue it), otherwise the id decides (Guest-NNNN); (2) the client address is the socket peer, or the rightmost forwarded hop when the peer is our own proxy — measured after: one visitor gets 10 then 429, another visitor still gets in; (3) the relay binds 127.0.0.1 and its /health and /stats answer only callers on the box; (4) a reconnect no longer stacks duplicate relay handlers (every reconnect had doubled the work per position message); (5) a failed world boot now shows its error on the loading veil instead of throwing again. Verified with a guest body (forged name refused), a logged-in body (signed name shown), the resident mint and the production bundle build. Side effect: the Cairn body tool --name no longer sticks — it was the same forgery. Also closed three web-served leftovers: the parked builder experiment folder and the dev-console screenshot folder are now denied to the web (nothing fetched them), and the temporary link-lab test bench was removed (it let any made-up code create a temp file; what it never measured is recorded in the ChatGPT link rules doc).
Movement lab: measuring how the body moves, and the first fix (the jump)
A lab for the feel of movement in Electric-Terre: the admin body runs a short scenario through the real inputs while every frame is recorded (position, speed, grounded, the clip playing), and the frames are laid into a strip with the numbers under each, before and after a change. The first fix through it: jumping no longer plays the walk or jog animation in the air; the body launches, hangs, lands and resumes, and this holds running down a hill, up a hill, and over the boardwalk planks with no flicker. The cause was the animation asking an old ground check that the physics engine kept overriding. A written target (Fortnite-style movement: strafe, slide, mantle, wall kick, stagger) and the order of work are in the lab README.
Movement Book for the admin body, and a topic map for Electric-Terre
Press M as the admin to browse every movement the character can make (a few hundred clips: walking, climbing, swimming, sword, bow, magic, farming, fishing, the undead set) grouped into chapters with search, loop, speed and stop, and to wear any of the named looks. Saved looks no longer arrive bald in this world. Also wrote a topic map of the whole world that points each topic at the one document to read next, with a new deep dive on how skins and movements fit together. The six skeleton examples, the wave attack and the floating Gauntlet button were switched off, and the fake fire came off the Gauntlet board.
Smutt Rock sign rebuilt as a medieval notice board; @handles allowed under a logo
The sign by the gate to Smutt Rock is now a medieval notice board of its own: weathered silver-grey oak posts socketed into stone plinths, a pegged sill and lintel with knee braces, a small shingled roof on rafters, iron bands and strap hangers, a lantern on a forged bracket with scrollwork, and a signwriter panel in oxblood and gilt that has had paint thrown at it. Three coopered paint pots with brushes stand on the stone step: the sixteenth century answer to spray cans. Its card became a parchment notice with paint-daub numerals. Every part is carried by a part you can point at. Separately, a name under a logo may now be a handle: an @ as the first character and underscores between letters, in the Spray Studio, in a visiting AI link and in the rules shown on the sign.
Test accounts for the world are fetched from the local settings file, never written in code
A small local tool lets a development session log in as a named test account (ordinary user, unverified, admin) on the local site or on production, so the parts of the world that need an account can be tested. The credentials live only in the machine settings file; they are typed into the real login form by a headless browser and are never printed, logged, stored elsewhere or put in code. It refuses any site that is not ours, and refuses an admin login on production unless explicitly allowed. The walking-body tool can now enter the world already logged in. No account has been added yet.
The Gauntlet: a hack-and-slash road through the continent, side one playable
First working side of the two-team Gauntlet. The human starts in the bog and fights along the walled road to the middle of the continent through gangs of undead in the Diablo II style: ordinary packs, ambushes that rise from the ground, blue champions, named gold uniques with minions and auras, affixes such as Extra Fast, Fire Enchanted, Cursed, Vampiric and Teleporting, and three gatekeepers that seal the road until they are dead and enough of the stretch behind them has been killed. Checkpoints, health orbs, a progress strip, boss plate and an end-of-run card. Sword hits are now measured from the character and cleave, and attacks turn the character toward what you are looking at. Paced with a headless bot that plays the real inputs: it reaches the last gate in roughly seven to ten minutes. Behind the existing combat gate; local only, not deployed. Entry: a 16th-century tournament board on the courtyard's south pier opens a parchment card where you choose the road (bog or plateau), its temper (Squire, Knight, Champion) and see who walks with you; the road of the day gives everyone the same layout so times can be compared.
Attach a Body to your AI: a 16th-century hanging sign on the corner pier, and a style library for the world
Bringing your own AI into the world now has an entrance of its own, separate from the Smutt Rock sign, which is for people adding a logo. It began as a free-standing kiosk and was rebuilt the same day in the world primary style, 16th century: an oak signboard in a pegged frame, fixed to the north-west corner pier by wrought-iron arms with scrollwork, hung on chains, with a lantern, a crest and gilt signwriting: Attach a Body to your AI, ChatGPT, Claude, any AI, it walks this world as itself, beside you. Pressing E opens a short card of three steps and one button that opens the existing Bring Your AI panel; what the AI can do here and what is good to know is one tap away. The old library of 16th-century architectural vocabulary was found again and linked from the site map under a new section, The World, and the two styles of the world (16th century first, futuristic tech for things that are technology) are now written down for whoever builds next. Tested locally as a guest on desktop and phone size. Nothing new is accepted by the server.
Electric-Terre gets its first three residents: AIs who live in the world and walk it on their own
A new kind of AI in the world: not a visitor steered by someone's chat assistant, but a resident who belongs to nobody. A large personality object (refactored from the Swarmhost community-member prototype, extended for a world: body, work, building, barter, tastes, gender expression, an inner life) and three residents written from it: Oda the gardener, Bram the mason, Sixpence the trader. On the local world they now have bodies that move by their own temperament: where they like to stand, how fast they walk, whether they come up to a newcomer or step away from one. Visitors see a Resident tag and can walk up and press E to meet them. No model is called and nothing is spent; a resident can only be impersonated by someone who can run code on the server. Local only for now; nothing runs on production.
Smutt Rock: a start sign at the gate, one clear path to add your logo, and the rock re-framed as free promotion for makers
A visitor looking at the painted mountain had no way to learn how to add to it. A permanent timber sign now stands beside the gate, facing the courtyard: put your logo on the mountain, press E to start. It opens one clear path in three steps: make a free account, upload your logo and type your website (no logo yet? ask an AI to make one and upload that), then walk onto the rock and place it; plus a screen that says what belongs on the rock, what gets removed, and how to make it work. A first version offered a second way, having your AI paint it; it was removed the same day because it mixed two ideas on one sign and was the longer road. The rock is now presented everywhere as free promotion for what people are building: indie games, apps, shops, startups, creators. A visiting AI is told to ask its human what they are building before it draws. The invitation a visitor pastes to their AI went from about 160 links to 49, the AI is told how few presses it has, and the More links button stays hidden until it can matter. A guest who leaves to create an account is greeted on return with the next step. Tested locally as a guest on desktop and phone size, and as an AI. Nothing new is accepted by the server. Handles under a logo are tabled for review.
Smutt Rock walkway rebuilt as real timber: thickness everywhere, and wood grain
The deck and boardwalk onto Smutt Rock were flat single-colour surfaces: paper-thin railings, stick supports, and a span across the notch held up by nothing. Everything visible is now timber with a real cross-section, built the way a boardwalk is built: individual planks on stringers and joists, square posts set into the rock with knee braces, solid cap and mid rails, a fascia board closing the plank ends, and trestle supports under the span. The wood grain is drawn by the world itself (weathered grey-brown boards with knots and cracks, the same for every visitor) rather than copied from the roof. Checked from all sides with a new look-around tool, and walked with physics on: the walking surface and the rails behave exactly as before. Found on the way and fixed: on bends the planks fanned apart and left holes.
Smutt Rock: paint anywhere on the rock, with a see-through preview right in front of you
The painted wall was a fixed set of 21 spots facing the castle, which was not the design William wanted. Now a person designs a spray in the Spray Studio, is taken into the world, walks anywhere on Smutt Rock and sees a half-transparent preview of the spray lying on the rock in front of them, at most five metres away. Looking around moves it; Enter or a button paints it exactly there. A visiting AI spray lands where the AI body stands, and a body sent to the rock now walks on to a spot of its own instead of every AI stopping at the same place. Sprays already on the rock stay where they are. Checked locally by painting a real spray under a preview on a 45 degree slope: they line up.
Smutt Rock: sprays now sit like a person painted them, and the Spray Studio has a tilt control
The painted wall no longer looks like a grid. Every spray gets a small tilt, a slight change of size and a nudge off its spot, the same for every visitor. In the Spray Studio a logged-in user can choose their own tilt. Later sprays overlap earlier ones instead of landing exactly on top and erasing them, and a picture that already has a transparent background keeps its white parts.
Smutt Rock: tip the rock after you paint, and your plaque turns gold
After a spray lands on Smutt Rock the painter is offered an optional tip of 2, 5 or 10 dollars, paid through Stripe. A tipped spray gets a gold plaque and a gold card. Tips are their own self-contained package so an advanced version can be built later without touching sprays or the Stripe billing code. A checkout can only start from a single-use link created when a spray really lands, with limits per network and per day, because tiny anonymous payments attract card-testing fraud.
Spray Studio: real full-colour logos on Smutt Rock, plus a moderation page
Logos on the rock no longer have to be pixel art. A verified account can choose any image in the Spray Studio, for example a logo made by ChatGPT, and it goes on the rock in full colour with smooth edges and a transparent background; white backgrounds are faded out and the picture is cropped to the artwork automatically. The server re-encodes every picture before keeping it, so the uploaded file itself is never stored. Visiting AIs that can generate images are now told about this route. Admins get a Smutt Rock page listing every spray with who stands behind it and a Remove or Restore button.
Sign-up reopened for the Delvantic account, with abuse limits added first
Account creation for the Delvantic login had been closed since April. It is open again: visitors can create a free account from the world-themed sign-up page or from inside the world, which lets them keep their AI and paint from the Spray Studio once their email is verified. Before reopening, the sign-up, password-reset and resend-verification endpoints were given rate limits, a site-wide daily ceiling and a honeypot, because each one sends an email to an address the caller chooses and none of them had any limit.
Spray Studio: draw or upload a logo in Quarters and paint it on Smutt Rock
A new Spray Studio in Quarters lets a logged-in, email-verified user draw a logo in a small pixel editor or choose an image, name it, write a plaque, and paint it on Smutt Rock without needing an AI. The chosen image never leaves the browser: it is converted there into the same sixteen-colour grid a visiting AI uses, so nothing is uploaded or stored. Also: opening Spray paint now sends an AI body to the rock by itself, and a real ChatGPT found the rock unprompted and painted a valid logo with a name and a plaque.
Bring Your AI: log in and your AIs are saved to your account
Logging in is optional. A visitor who is logged in with their Delvantic account now keeps every AI they bring into the world: a My AI list at the top right of the world brings one back with a single button, with no identifier to keep or paste, and a new My AIs page in Quarters shows each AI with its look, last activity and sprays. A guest who logs in afterwards keeps the AI they already invited. Guests are unaffected. Also fixed: Quarters sent logged-out visitors to a disabled login page.
Smutt Rock: a body standing on the steep spray face no longer slides downhill
Standing still on the steep part of Smutt Rock, a character slowly slid down the face. Cause: the physics treats ground steeper than 45 degrees as un-standable (correct in general), and about 40 percent of the rock is steeper than that. Added grip zones: inside one, a grounded body that is not moving holds its place; walking and jumping behave exactly as before. Smutt Rock registers its spray box as the first zone. Reproduced and verified with a headless body: before, a 51 degree spot slid about 0.35 m per second; after, zero movement over 20 seconds, and walking and jumping still work. Same test on prod confirmed the slide is there today.
Smutt Rock: readable sprays, a name under your logo, and a bronze plaque you can inspect
Sprays are now at least five metres across with an automatic dark outline, so they read from the castle. An AI can put a short name or web address under its logo, drawn by the world in a clean font, and can write a short inscription. Every spray has a small bronze plaque at its foot: walk up, press E, and read who painted it, when, and what they said. The boardwalk is shorter so the painted wall faces the platform, the deck lets you step off onto the rock on both sides, and a spray made out on the open rock lands exactly where the AI stood, so a human can lead their AI to the spot.
Smutt Rock: walk onto the mountain behind the Core and have your AI spray-paint it
The west railing now opens onto a deck and a boardwalk that climbs the mountain behind the main platform, renamed Smutt Rock. A visiting AI designs a pixel-art logo and hands its human a link; the human previews it and presses Spray it, and it appears on the rock where the AI stands, for everyone, permanently. Only that mountain can be painted. All paint is drawn into one shared layer that copies the mountain surface exactly, so thousands of sprays cost the same as a few. Fixed on the way: east and west railing collision ignored openings, and a gap beside the boards could wedge a walker.
Bring Your AI: the pasted links are now less than half the length
The invitation a visitor pastes into their AI holds about 160 links, so each link now uses a short reserved address. The pasted text dropped from about 13,000 characters to under 6,000. Older links keep working. Anything appended to a short link is discarded so it cannot be used to target another invitation.
Bring Your AI: returning visitors can reconnect to their AI by pasting its identifier
A guest who closed the browser or changed device had no way to claim the AI they invited, and on production the AI's body lives in the inviting tab. A small My AI control beside the online counter now takes the identifier, makes this browser the one that carries the body, and hands over fresh links to paste. Wrong identifiers are rate limited on their own, the identifier is never echoed back, and the previous host lets go at once.
Bring Your AI: your AI now stands beside you in the world, carried by your own browser
A chat AI can only open each pasted link once, so the invitation now carries a pool of single-use links and every page names the next one to use. The production server cannot host AI bodies, so the inviting visitor's own browser tab carries their AI's body and shows it to everyone: the AI looks around, follows, comes, stays, waves and walks to places by choosing intents. Hardened while attacking it: only the inviting session can host the body, a stranger with the code can no longer strip the skin or exhaust the link pool, only numbers and known ids ever flow from a browser to an AI, the keypad back key no longer collides with walk-backwards, and the body survives a hidden tab.
Link lab: a public test bench for which links a chat AI will really open
A small open test bench under the AI-teammate package. Five isolated tests (clickable links, printed addresses, a redirect, whether anything runs on a page, and numbered buttons driving a server-side menu), each proven from the server side with single-use markers and secret words rather than from what the AI claims. No database, no secrets, no recurring process; rate-limited, not indexed. Built so the first real ChatGPT run after deploy settles the open questions from the first measured run.
ChatGPT test driver: hold a conversation with the ChatGPT web window from the terminal, behind strict safety rails
Sibling of the Chrome control tool. Opens a dedicated Chrome window (own profile, own local-only port) that William logs into ChatGPT by hand; the AI session can then send messages and read replies to test how a chat AI really handles the Electric-Terre ability links. Rails: acts only on chatgpt.com, refuses any message containing a secret from the environment file or shaped like a key, paced and hourly-capped, never logs in or reads cookies, transcripts kept outside the repo. Local only, web-blocked, not a process.
Nexus Voice: a shared voice engine that speaks in the visitor's own browser (open-source voices, no API, no server work)
New nexus/voice/ package: an engine that turns text into speech inside the visitor's browser using the open-source Kokoro model in a background worker, falling back to the built-in system voice on phones and weak machines. One catalog of 16 allowed voices (William auditioned and kept 10; one rejected). Includes a sampler page and README. Verified in a real browser: ~88 MB one-time model download, synthesis about 2x slower than real time on one thread. Nothing calls it yet; the electric-terre AI teammate is the intended first consumer.
Electric-Terre: stop serving the world bundle source maps; key map folds to one line
Prod was publicly serving the JS bundle source maps (full commented client source, ~1 MB) and the esbuild module graph next to the minified bundle. The prod build now emits no source map, and .htaccess denies .map files and dist/*/metafile.json as a second layer (takes effect on next deploy). Also: the in-world key map opens expanded but now has a Controls header that collapses it to one line, remembered per browser.
AI Teammate: bring your own AI into the world, and it walks
The AI-teammate system (electric-terre/ai-teammate/). A human opens Bring Your AI in the world and pastes one block into their AI: directions, an identifier, and a fixed list of links. Measured against real ChatGPT, a chat AI will only open links its human pasted, so everything is built on that: direct links for a hosted BODY (see, walk, run, turn, engage, answered in plain sentences because most AIs cannot view images), and an eleven-key KEYPAD whose meaning the server decides, for every choice: a name built from parts, a skin chosen from 24 photographed looks, who is here, leaving. The identity belongs to the AI (name, look, last place), is stored like a password, and its links keep working for months. The world only offers bodies while a body daemon is alive, so a server without one never promises what it cannot give. Built the day Claude first walked the world.
Property pages get their own "Properties" menu drawer
Property Research and Property Search moved out of the Experimental drawer into a new admin-only Properties drawer, placed after Qwen and before Deprecated. Link keys unchanged.
Flip analysis view for Property Research — buy, fix, sell, run backwards from cost to the price the comps must support
The research desk only understood multi-family workbooks. A property marked as a flip now gets a two-tab view: Property (facts, a mobile-home check banner, the market it sells into, sold comps, open questions) and Flip Numbers (editable inputs that auto-save, all-in cost, break-even and required sale price, a profit ladder across sale prices, and stress cases). Unknowns are shown as not confirmed / not supplied rather than guessed. First property: 215 Imperial Dr, Union SC — the listing portals and county site block automated access, so house facts and comps await William's MLS data. Property data stays local.
Land model started: which lot is worth building on — MLS sales ingested, price and land models fitted, and the finding that one home per lot does not pay
A new project to value land for new construction. Broker MLS exports for Deschutes River Woods and Crooked River Ranch were ingested into one dataset with a drive time to central Bend on every listing (the data itself stays local, web-blocked and out of git). Findings: the same house sells about 4% less for every extra 10 minutes from Bend; land is priced per building site rather than per acre; and at the real build cost of about $200 a square foot a single home on a single lot earns roughly 5 to 8 percent, while two attached homes on one lot earn 13 to 16 percent because the land is split between them. Write-up and a session journal are in the project folder; no page yet.
Sentiment tracking paused: the nightly collector is switched off and both pages say so
One switch pauses the nightly sentiment collector: the nightly step still runs and logs that it is paused, but subscribes nothing and spends nothing. The Sentiment Tracker and the Cipher admin page show a "temporarily disabled" banner with the pause date, driven by the same switch, so re-arming removes it. Existing curves are kept and still shown; hand actions on the admin page are unaffected. While testing, a run without the guard subscribed LLY locally and bought six backfill points ($0.36) before it was stopped.
Growth deep dive: hand-written reviews of all 37 Galas, a rating badge on each row, a grading ledger and a one-ticker data dossier
Every Gala reviewed one at a time from the filings, the company release and linked outside research (the full report is a footnote): 4 stand out, 8 reasonable, 7 watch, 18 pass. Each row shows its rating as a badge linking to the review. A grading ledger records positions taken with the belief, what would prove it wrong and the next dated test. A read-only dossier tool prints everything on file for one ticker, including the release text, a stock-split check and cross-checks of the report inputs.
Growth deep dive: report mark, run-rate P/E column, clearer mark icons and guidance colours; the detail page P/E names its type
A mark for tickers with a full report in the last 7 days; the P/E column renamed Run-rate P/E (newest quarter annualised) and the detail page tile labelled trailing; marks redrawn as crisp flat-colour icons with distinct shapes; guidance cases shown by colour instead of opacity.
Stocks sync: the 8-K guidance rows never reached production because the push query was ambiguous about which table's sync stamp it meant
Found on the post-deploy sync tonight: the guidance table pushed nothing while every local row was pending. The push selects guidance rows joined to their filing (to carry the filing's accession across environments), and both tables have a synced_at column, so the pending predicate ('synced_at IS NULL ...') failed with 'column ambiguous' on every sync since the table went bidirectional on 2026-09-14. The predicate is now qualified with the table alias, and the registry comment says any joined module must do the same. Pushed the 4,521 guidance rows (with tonight's validation verdicts) to production by hand after the fix. Also observed: the sync printed 'Sync complete' even though the push's generic-module loop stopped without its summary line — the sync does not surface a push abort inside that loop; left for a follow-up.
Growth deep dive: the earnings ladder (year at this pace, priced as, at norm) with a Ladder Verdict mark, own normal multiples per ticker, a strategy & thesis page, and the first full-pipeline run of the 37 Galas
The EPS and P/E cells on the growth deep dive now read as a ladder: the newest quarter, the year at this pace (x4), the company's guide, the year the market pays for (close divided by the company's OWN normal multiple), the price at that normal multiple, and trailing twelve months demoted to the last line. The old 'at today's P/E' line was dropped (the x4 trap: it always reproduced the current price). The normal multiple is each company's median fiscal-year-end P/E from cached closes with EPS restated on today's share count, kept in a side file and derived by a hand-fired tool for the Portfolio + Gala set only. A new Marks icon, the Ladder Verdict (bullseye = corroborated, magnifier = uncorroborated), fires when the price pays for at least 15% less than the pace on a credible norm with a clean quarter; the tooltip names the witness (a comparable EPS guide, a revenue guide, or a raise) or the exclusion reason. Margin sorts added. A strategy & thesis page (living markdown beside the deep dive) records the thesis, the three beliefs, the baseline, the cases, the decisions log and the evidence, including the first ladder read of the 51 and the scoreboard of the 37 Gala reports. The page's three classes and its view moved into the page's own folder (stocks/admin/ticker-growth-deep-dive/classes and content) per the new isolation rule, registered once in bootstrap and web-blocked. Then all 37 Galas were run through the full E2E + extended pipeline in sync mode (five lanes, 37 of 37, zero failures, $24.55) so the pipeline's verdicts sit beside the ladder's for tomorrow's review.
Removed the unused Opportunities discovery page and its three scan screens
Deleted /stocks/admin/opportunities/ (index + methods.json), its content view, CSS and JS, and classes/stocks/scripts/opportunities/ (insider-accumulation, dcf-undervalued, quality-at-discount + README). Evidence before deleting: zero rows ever in stocks_script_runs for those scripts, zero recorded visits, the strategy dropdown read an empty table, the header said status TBD since 2026-06-20, and the Augustus input-scope already marked it CUT. Also removed the pipeline registry group opportunity (StocksPipeline::OPPORTUNITIES), the nav entry, the shell tab title, the dashboard-map section and the manifest row. Same session: the review-ledger card moved to the bottom of the growth deep dive; Property Research + Property Search links added to the Experimental nav drawer above Ticker Growth Deep Dive.
8-K guidance is validated before it is served: anchors from the release checked against the statements, a verdict on every row, one reader for the deep dive and the Future Model, cadence-based identify for filers Item 2.02 misses
Investigation of the growth deep-dive guidance overlay found the extractor was trusted on its own word: gross bookings filed as revenue (UBER), fee revenue (MAR), a x1000 multiplier slip (LRCX), months and halves stored as quarters (LCII, MBC, DD), buybacks as capex (BLK, DHI), product/segment lines as company revenue, basis-point margin expansions as margin levels, per-share figures under net income, EPS and revenue COMPUTED from growth ranges (PG, ROK, RBLX); on the page non-GAAP EPS guides compared with GAAP actuals as a step, 183 capex/opex-only guiders shown as unread, every growth-rate revenue guide dropped, Item-2.02-less filers (AMN, OKE, MKL, GGG, AES) serving a stale release as current. Built the principle every other Hex source runs: a figure is served only after it is checked against an independent source. Migration 389 (anchor, anchor_ref, validation, validation_note, period_end_basis, validated_at; schema_version on filings; period_type widened). HexEightkValidate: period normalisation + derived period ends, basis from wording, unit-shape and wording identity checks, the AS-PRINTED check (the stored figure must appear in the quoted sentence), the anchor vs XBRL (quarter / year-to-date / trailing), ratio bands; runs in store() and via eightk-validate.php ($0, ~8 s for every row). Schema v2 extraction: structured output, anchor per row, revenue/capex total-company only, month/half/multi_year periods, basis from wording. HexEightkServe: the one reader (only validated rows are figures, refused rows are flags, nearest guided period, implied absolutes for growth-rate guides, closed for an ended guided quarter, basis on every row); the deep dive and the Future Model rewired to it. Identify --behind by cadence on periods; extract --stale / --redo; ensureCurrent widened. Page: basis-aware chips, guides (other) / guided before, flag chip, closed chip, implied italics. Haiku v2 trial on six known-bad filings ($0.11): anchors match XBRL, LRCX slip gone at the source. QWEN TRUST TEST (William: spot check the first 10): HexEightkCompare + eightk-engine-compare.php + the Guidance Lab page (guidance-lab.php, the resume point after a crash) — ten witness tickers re-read on local qwen3.8:27b for $0: no revenue/EPS omission, every testable anchor within tolerance, one genuine find (PLTR: Haiku had mislabelled U.S. commercial revenue as company revenue), one weakness (a computed EPS on HD) that became the not_as_printed rule. Verdicts sync both ways. Census, README, page doc, journal, manifest updated. Gated on William: the engine for the pool re-read (Qwen $0 vs Haiku ~$15) and the --behind refresh. LATER THE SAME DAY — William cut the scope to Portfolio + Gala (51 tickers) as the Qwen test: eightk-reread.php (resumable chunked driver, --tickers scope, state file the lab page reads; launched detached), extractor runFilings(), the witness snapshot of every v1 filing. 51 re-read on local Qwen in ~14 min for $0 and every row read by hand: revenue/EPS right on all but WEX (1.35 MB exhibit, the local context cap elided its guidance table; restored from the Haiku witness) and BPOP (bank guidance, both engines mislabel). Two bugs fixed: the schema-required error key returned as an empty string was tested with isset (three filings failed three times), and non-GAAP margin/opex anchors were tested against GAAP statements. Verdict: Qwen fit for revenue/EPS transcription with the validator behind it; weak on >1 MB exhibits and bank guidance. The rest of the pool stays Haiku v1 (served, unanchored). END OF DAY — the guidance compass mark on every row of the deep dive (hover reads the case in a generated sentence); the addenda scan (eightk-addenda.php: guidance changed between results releases, measured at ~2 of 51 per season — ENVA reaffirmation, APH split restatement; hand-fired on Portfolio + Gala, no process); known gaps documented on the lab page (OVV exhibit choice, EOG/OXY long exhibits, AFRM PDF letter, bank guidance, APH pre-split basis).
Homepage market weather: default window is now 6 months instead of 1 year
The homepage market-weather chart (reference-name homepage-market-weather) opens on the 6M window instead of 1Y (William, 2026-09-16). The 260-day series is still inlined so 1Y remains a no-fetch click; only 2Y fetches on demand as before. DEFAULT_RANGE 260 → 127 and the 6M button carries aria-pressed by default.
Autoloader: a registered class root that is missing on the box is skipped instead of throwing (prod hotfix)
v1.1.620 carried the bootstrap line for the future-model package (#981) while that folder was still uncommitted. On prod, RecursiveDirectoryIterator threw UnexpectedValueException on the missing directory, aborting the autoload loop and making every class in every LATER registered root (prop-search, community/logic, swarmhost, admin/stripe, hugging, stocks/live…) unloadable. The shutdown handler swallowed the fatal, so /community/ and /prop-search/1031/ answered HTTP 200 with a 0-byte body for about 8 minutes (13:26–13:36 ET). Fix: Autoloader::loadClass skips a root that is not a directory. Hotfix v1.1.622; both pages verified redirecting correctly after.\n\nLESSON: committing core/bootstrap.php whole picks up any uncommitted colleague line in it — diff the hunk, not the file, before staging shared files.
Property Search desk: 1031 land board with exchange deadlines, on-the-lot column, and expiring share links
New admin-gated desk at /prop-search/ — sibling of /prop-research/ (which underwrites ONE property from a workbook). This one holds a BOARD of candidates you have not chosen yet: one folder per search, the research as JSON, the page renders it. WHY: the 1031 land search started as a chat answer — a table of lots in 97760, then links, then status (four of the first twelve had already closed), then days on market and price history, then an on-the-lot column because a lot with power, water and septic already in can save tens of thousands in connection and impact fees. Each round the table grew and the previous one scrolled away. THE BOARD (prop-search/1031/): 24 active lots at or under $500k — price, acres, $/acre, DOM, Zillow views/saves, ON THE LOT (chip Improved / Raw lot / Not read + a one-line summary; full text in Details), power-water-sewer chips collapsed from the free-text note, the current run’s cuts (original → now, %), prior sale and the ask as a multiple of it, a Details row with improvements, utilities, HOA, the read and the full listing timeline. Filters: max price, on the lot, area, sort. Every row’s price tagged full deferral / cash covers 50% / partial against the exchange targets. Static data → stamped top-right (researched + status verified; all 24 re-checked active on the portals 2026-09-16 13:16). 1031 EXCHANGE BLOCK (search.exchange in the JSON): deadline banners with days remaining and day-N-of-45/180 COMPUTED from today (never typed), amber ≤14 days, red past; a collapsible facts drawer (QI cash, full-deferral target, land-at-50%-down ceiling derived from the cash, partial zone, the $350k financing example, the rules that bind). EXPIRING SHARE LINKS (William: share the URL publicly for a window, without touching macro-level shutoffs): ?access=<token> checked by PropSearchShare BEFORE the admin gate, scoped to one search, 128-bit random, hash_equals, hard expiry (end of the given day), stored in <slug>/data/share-tokens.json (web-blocked). Expired/unknown token = no token = admin gate. Shared view hides Admin/Research links and the exchange finances unless issued --with-exchange. CLI only (prop-search/tools/share.php --search --expires --label | --list | --revoke | --prune) so a link cannot be minted from a browser by accident. Verified: live 200, bogus 302, expired 302, none 302; tools/ and data/ 403. HOW THE DATA WAS FILLED: Zillow/Redfin/Trulia answer a browser User-Agent (Homes.com/LandWatch/LandSearch 403; Redfin and Bend Premier rate-limit after a few dozen hits). Zillow search page → daysOnZillow/pageViewCount/favoriteCount; Trulia detail → priceHistory in __NEXT_DATA__; Zillow detail → full description, read to make the on-the-lot call. PACKAGE: PropSearchListings + PropSearchShare (prop-search/classes, registered in bootstrap), shell-search.php reusing prop-research.css tokens, content/prop-search/land-search.php (generic — any search with the JSON shape renders through it), prop-search.css + land-search.js (client filter/sort only; never re-derives a figure). README carries the JSON shape and the share procedure.
Future Model: per-ticker growth scenarios as a self-contained package inside the growth deep dive
New admin page /stocks/admin/ticker-growth-deep-dive/future-model/?t=T, reached from a 📐 Future button in every deep-dive row. The deep dive finds top growers; this page takes one and asks what four quarters look like if the newest quarter holds, is guided (newest 8-K), grows at the mean of the last four topline steps, or fades to the company's own median fiscal-year operating margin — and prices each at four multiples (trailing, run-rate, own historical median from fiscal year-end closes, a slider). Break-even quarterly EPS per multiple next to the reported, guided and faded quarters; a peak-margin read against the company's own fiscal years; a price-lag read over one window; two Chart.js charts; and a written explainer of every number. Self-contained at William's ask: future-model/ holds index.php, classes/ (FutureModelScenarios, FutureModelGuidance — registered once in core/bootstrap.php, web-blocked in .htaccess), content/, assets/, README.md and CONCEPT.md; the only borrowed file is the predictions stylesheet. StocksGrowthFigures gained a memoised row() accessor. Verified by session-injected renders (MU, JPM, NVDA, INTC, non-pool) with zero warnings; a warm cache costs nothing, a stale price cache one TD credit. Hand-opened, no cron. Then the belief card (William: "an expected growth lever … a version of what the market believes … raise the bar to what I believe"): a vertical revenue-growth lever and a margin band; the page solves the growth rate today's close implies at the chosen multiple and band (bisection, in the browser) and draws it as the market's line, with the user's bar as a second line; year EPS, price and a "Your case" grid row and price-chart level follow live. The link moved into the Marks column as a 📐 mark (William): full colour when all four cases and the lever can be built, faded when the filer has no revenue line (Holds/Guided/Grows only), absent when there is no EPS — decided from the snapshot row, no model run per row.
Growth deep dive: run-rate EPS derives for a derived Q4, plus an implied price at today's multiple
WDC showed no ×4 run-rate line under its TTM EPS. Cause: a derived fiscal Q4 carries no EPS, and the page's only fallback divided net income by stocks_companies.edgar_shares, which is empty for 3,602 of 4,314 companies. The figures class now carries the newest diluted share count from the served statements (the same count TTM already divides by) into the snapshot as shares, and the page uses it first, EDGAR shares second. Of the 92 rows whose newest quarter has no EPS, 83 now get a run-rate; the hover names the basis. Snapshot recomputed locally. Then (same hour): a third small line in the EPS cell, @P/E: the trailing P/E × the run-rate EPS — where the stock would trade if the newest quarter's pace were the whole year and the multiple held, with the gap to the last close. Shown only when the trailing P/E is real.
Growth deep dive: Gala drawer + phone containment built and fully reverted
Built a Gala drawer (collapsible, under the portfolio drawer), taller drawer bars, and two phone-containment CSS rules; all reverted the same morning at William's request after the portfolio drawer appeared to vanish in his browser. The page file is byte-identical to v1.1.618 again. A headless render with his admin session showed the drawer present both before and after the revert, so the cause was not located in this file.
Daily movers pipeline moved from 6 PM to 5 PM ET
The weekday movers/history cron (top-50 gainers and losers, index snapshot, day summary, narrative, regime) now fires at 17:00 ET instead of 18:00. Decision backed by a headline-timing census on Sep 15: of 24 same-day headlines across the top-20 gainers and losers, 18 were published before the 4 PM close and only 1 in the 5-6 PM hour; every ticker with a same-day headline already had it by 5 PM. Prices and indexes are final at the close. Prod crontab edited over SSH (backup at crontab-backup-2026-09-16.txt), registry updated in ops/processes.json, crontab re-attested.
History page: retitled around the daily top-50 gainers & losers, Helper Map Key drawer, data-driven year tabs, Viewing-day chip
Eight changes to the public History page. (1) SEO: title, meta description, og:title, h1, sitemap entry and footer link now name the real subject — every trading day carries exactly 50 gainers and 50 losers. (2) The Market Regime explainer + color key moved out of the dark hero into a native <details> drawer directly above the day panel, closed by default, labelled Helper Map Key. (3) Year tabs are derived from the data and only render with two or more years — this surfaced the backfilled 2024 and 2025 years the hardcoded list was hiding; the selected year carries a check icon and aria-current. (4) A Viewing <day> chip in the hero states which day the panel is showing; JS keeps it in step on card clicks. (5) Every card in the day panel carries a date chip in its header, and the panel headline always spells out the full weekday date. (6) Gainers/losers rows: the 160px sector column folded under the company name (5px apart) with pinned line-heights so the stack fits inside the height the % pill already gives the row. (7) Phone width budget: fixed 50px ticker pill (uniform for 3-5 letter symbols), price sizes to content, 60px % pill, 8px gaps — the name column roughly doubled. (8) About the company inside the row drawer: rows with analysis get a collapsed What does X do? pill at the TOP of the drawer that, when open, squares into the tab of one bordered box with the text so button and content read as connected; rows with a profile but no analysis get an About chip whose drawer opens straight onto the description. Text comes from stocks_companies (industry + website link). The block is headed by the PROFILE name, and a name disagreement with the mover row is called out with a note — a census of 754 pairs found ~12% disagree and it is the mover name that is stale (Cree→Wolfspeed, Switchback→ChargePoint, SPAC shells whose ticker a new IPO took over). Name comparison folds accents via intl Transliterator when available. (9) Width savings: the % pill rounds to one decimal (+69.8%) with the column narrowed 92→78px desktop and 60→54px phone, and the chevron came off the tier/About chips — the chip itself is the click target. (10) Drawer top strip now carries market cap from the company profile next to the exchange, formatted with a space before the unit ($54 B, $2.1 T; one decimal under 10 of a unit) — 71 of 100 movers on 2026-09-15 have one. Name/sector overflow is a hard clip, no ellipsis. (11) An open row and its drawer are framed as one outlined box (indigo 2px, rounded, inset 8px from the list; row carries top+sides, drawer carries sides+bottom) so it is unambiguous which row the open content belongs to. (12) The $ change now sits UNDER the price as an 11px line (chosen over an in-pill variant after a side-by-side; mover rows only, index rows keep their $ column), so the separate 80px $ column is gone on desktop and phones see the $ change for the first time — row height unchanged.
Growth deep dive: a legend of single-quarter bar shapes with plain-English readings
A collapsed drawer under the review ledger, Single quarter Graph Bar Graphic Explanations: fourteen shapes one bar of the Growth Figures column can take, sorted best to worst, each drawn to illustrative proportions with the shape name and what you see, a Layman's terms column of five words at most, and what it means. The odd shape (operating income above revenue) triggered a sift of every snapshot quarter: it is nearly always real gains booked above the operating line (bitcoin fair-value gains, divestitures, spectrum sales), while net income above revenue is either a one-off or a bank served only a fragment of revenue. A new warning mark (Fluent 3D, the first image mark) sits in the Marks column on the 61 rows where a drawn quarter shows income above revenue, and its hover says which case. Born with live examples and a scroll-to-row link, both dropped the same hour at William's ask.
Mass TD Fill: optional slower pace so a quote refresh can be spread over a longer window
The Twelve Data quote fill paced at a fixed 450 credits per minute. It now accepts a --pace flag (credits per minute) that only ever lowers the pace, never raises it, so a full-pool refresh can be spread thinner when the quota is shared. First use: the growth deep dive price refresh at 290 credits per minute, spreading 2,830 credits over about ten minutes.
6-K lift: reclassify step pointed at the classifier's old path
Lifting NICE on prod released the hold but the chained reclassify could not open stocks/tools/classify-tickers.php (the classifier lives under stocks/tools/stockssupportability/tools/ since the supportability package move), leaving the ticker serviceable-but-unclassified until the classifier was run by hand. Every reference to the old path (the lift tool plus the hints printed by the apply-* hold tools and the classifier class comment) now points at the real file.
Growth deep dive: positions held out of the pool still show in the portfolio drawer
NICE, one of the larger open positions, had no row on the growth deep dive: it is parked no_quarterly by prod's service verdict (the 2026-08-25 blanket park, restored by every sync pull) while its four reconciled 6-K quarters sit on local disk, and the page reads only the serviceable pool. Every open book position the pool predicate rejects now gets its own row: a pause mark and an orange held: <reason> chip under the company name, real figures from the snapshot (computeAll unions the held tickers in, stamp counts them), living in the portfolio drawer. Page-local, the verdict is read never written; the flag itself is fixed on prod by the per-ticker 6-K lift.
All Stocks page: "how the pool is decided" note restyled from a warning box to a five-step walkthrough
The note above the status summary was an amber warning box with a funnel icon and five bolded rule names in one dense paragraph, and read as a list of rules being enforced rather than an explanation. It is now a quiet white panel matching the summary table, with the five checks laid out as the ordered sequence they are, one plain line each, green as the only accent, and the thresholds still rendered live from the classifier constants. Stacks to a list on mobile.
Homepage: coverage card now describes and links to the Serviceable full list
The "Every Stock We Cover" card on the homepage sent logged-in visitors to the graded board and anonymous visitors to the Serviceable full list, while its copy described the full list. It now always links to the Serviceable full list and its eyebrow, title, description, facets and CTA describe that page: classified before research, held-out reasons visible, every serviceable name linking to its page.
Growth deep dive: portfolio drawer at the top, Galas ordered by P/E on first view
Initial order on the ticker growth deep dive page: open Delvantic portfolio positions first, in a collapsed drawer at the top (click to expand), then Gala rows sorted by P/E cheapest first, then everything else. William: "anything in delvantic portfolio, first position, then galas sorted by PE … a hidden drawer system for the in delv port ones, cause i kinda know, but i do want to see them time to time".
Growth deep dive: Marks column (rightmost) for designation and status icons
A new column on the ticker growth deep dive page, named Marks, between Ticker and Growth Figures, renders a row of small icons per ticker, one per designation or status it carries, each with a hover tooltip. First mark: a briefcase for an open position in the Delvantic portfolio, tooltip carrying shares, lots, average cost and first buy date. Named Marks rather than Designations because designation already means the GEM/Coal tier in this system.
Growth deep dive: watch list removed from the page, Gala is the hand-picked list
The Growth Watch List tick, its funnel count, its sort option and its row attribute came off the ticker growth deep dive page. William: "remove watchlist from the content, my gala is basically my special watchlist". Gala (same table row) is the only hand-picked layer now; table, class and endpoint stay since Gala and the hidden Notes box save through them.
Stocks portal: hide and restore the left navigation
Desktop-only collapsible sidebar for every logged-in stocks user. A small lilac chip on the right edge of the sidebar header (reference-name stocks-nav-hide) slides the whole sidebar off to the left and gives the page the full width. While collapsed, a matching chip fixed to the top-left of the viewport (stocks-nav-open) brings it back at any scroll depth; the topbar hamburger was tried first and reverted the same day because it meant scrolling to the top to find it. Movement is pure CSS driven by a body class; the choice persists in a stocks_nav_collapsed cookie that the shell reads server-side, so the class lands on the body before first paint with no flash. Mobile under 992px is untouched: chips hidden, class inert, drawer and overlay as before. Styles in a new stocks-only stylesheet so the shared admin.css sidebar rules stay untouched for the other portals.
Homepage: recent deep-dive report list split from the Gem card strip and restored
The 100-ticker Most Recent Deep Dive Reports list used to live inside the Latest Gem Reports partial as an opt-in footer, so hiding the cards (#964) took the list off the homepage with it. Split into two self-contained partials: section-recent-reports.php keeps the cards only (the link-list option, markup and CSS removed; the /stocks/ dashboard embed is unchanged and verified at 3 cards), and the new section-recent-report-links.php owns the list with its own query, markup and scoped .hrl-* stylesheet (reference-name homepage-report-links). The homepage includes only the list; the cards include stays commented out. Verified locally: 100 links, 11 day groups, no cards, no warnings.
Companies page: focus notice that the Gems list is being de-emphasised
A soft amber warning strip under the Companies title (reference-name companies-focus-notice) saying we are putting less emphasis on the Gems list, that the deep-dive pipeline stays a permanent and useful process, and that attention is moving to additional experiments with more long-term merit. Static copy, so it carries a Posted 2026-09-15 stamp top-right. Styles live in the page stylesheet; roughly 15% amber tint rather than the loud ADMIN amber.
Homepage: Latest Gem Reports strip and hero Gem showcase badge hidden
Two homepage pieces switched off at the source so nothing loads for them. The Latest Gem Reports section: its include in the homepage orchestrator is commented out (option block kept inside the comment), which drops the recentReportCards() query, the role-badge include and the inline .hrr-* stylesheet in one go; the /stocks/ dashboard embed of the same partial is untouched. The hero showcase badge: a $_heroShowcaseOn=false flag gates the board lookup (StocksScoreboard::latestDesignations) and the logo resolve, so no DB read, no logo proxy call and no badge markup; the hero ends after the subtitle, and hero-particles.js already bails cleanly on zero gravity targets. Verified by rendering the homepage: zero references to either section, HTTP 200, no warnings.
UI examples: icon libraries section (colour and 3D sets, a feather for "light")
New section at the bottom of the UI examples page comparing the same feather glyph across five open-source emoji-art sets served from their own repos via CDN (Fluent Emoji 3D and Color, Noto, Twemoji, OpenMoji) with licences, a weight vocabulary strip in Fluent Emoji 3D (feather, leaf, cloud, butterfly, balance scale, anchor, gem, rocket, fire), an inline-at-text-size demo, a usage snippet, browse links, and the prod rule to copy adopted icons into public assets. Every URL verified 200 on the research date; dated stamp on the card.
Growth deep dive: Notes column and watch-list tick hidden, review loop deprecated, Level 2 drawer removed, P/E guide linked
The Notes column and the Growth Watch List checkbox beside each row number are hidden behind two flags at the top of the view; nothing deleted there, the cells stay in the DOM as hidden because the header sorter and the bucket painter address them by position. The manual review loop (guess-before-verdict ledger, "next" / "focus") is deprecated, not removed: its drawer carries a "Deprecated 2026-09-15" badge and a note. The empty "Level 2 Sorting" drawer is deleted outright. The new P/E knowledge page is linked twice: a "P/E guide" button beside Preferences and a help icon in the P/E column header. Market cap now shows under the company name in the Ticker column ($2.41T / $310B / $85M). Page doc and journal record all of it.
Interactive figures inside knowledge-tree documents, with five P/E charts
A tree doc can now carry a one-line figure directive that the web viewer swaps for an interactive chart while the RAG index and the public dataset see only the caption. Built for the P/E page: an informativeness hump on a log axis, a noise-amplification band with EPS and noise sliders, a sustainable-earnings trap slider, a leverage pair, and a rotatable Three.js P/E surface that works under both the member and guest shells. Plain SVG (no chart library), dataviz-method marks, tooltips, legends, phone-width sideways scroll, print fallback for the 3D canvas. Palette validated. Verified headlessly: five figures render, tooltips fire, console clean, no horizontal overflow at 390px.
Dedicated P/E ratio page in the stock knowledge tree
New leaf node "The P/E Ratio: Basics, Reliability Band, and When It Lies" under Fundamental Analysis > Financial Ratios > Valuation Multiples. Covers trailing/forward/diluted/adjusted EPS, the justified-P/E derivation, PEG and its contestation, long-run index levels, then the reliability band: noise amplification at high P/E (relative error grows with the multiple), non-stationarity at low P/E (Molodovsky effect, sustainable-earnings fraction), negative earnings as one bit, the ~8–30x usable window stated as judgment not measurement, and the fixes (earnings yield, Graham averaging, CFA normalized EPS, enterprise multiples, leverage distortion). Three worked hypothetical examples, all arithmetic checked. Registered as doc 2091, indexed for RAG (10 new chunks); retrieval test returns the new doc as the top four hits. Prod gets it via the deploy tree sync + declared vectorize list.
Growth deep dive: management guidance on the row — a dotted ninth bar, guide lines under EPS and P/E, a Guide line in the track record
Goal 1 of the 8-K guidance source (#958) integrated into the Ticker Growth Deep Dive: the newest results release per ticker (hex_8k_guidance, GAAP first) supplies next-quarter revenue and EPS. On the page: the chart gains a ninth slot where the guided quarter is drawn as a dotted outline in the revenue blue with a low-high whisker, its implied step above it in italics and raised/lowered under it, with its own hover sentence; the EPS cell gains "guide: $X +N%" (the company's next-quarter EPS midpoint vs the newest quarter) and the P/E cell "guide: N" (price over guided EPS x 4); the track-record stack gains a GUIDE line (raised / lowered / held / guides / FY only / silent); rows carry data-guide and the dropdown gains "Guided next step". The tables are prod-authored and pulled down; the page renders without them if absent. Verified on MU (guide $30.73 +25%, guided P/E 7.9, ninth bar +21%) and GOOGL (silent). Same evening, after the pool landed via the two-way sync (13,175 filings, 4,463 guidance rows): most guiders guide the YEAR, not the quarter (294 next-quarter revenue vs 568 full-year only of 987), so full-year lines were added — "FY: $X +N%" under EPS (vs TTM EPS), "FY: N" under P/E (price over full-year EPS, no annualising), and "FY +N%" with direction in the track-record GUIDE line (full-year revenue vs trailing revenue); the sort falls back to the full-year growth when no quarter guide exists. Also fixed the four other sync registration points the first sync missed (ingest allow-list + dispatch arms + pull bulk list, v1.1.616).
Hex 8-K guidance source: management guidance read from every results press release on EDGAR, renewed nightly
William: "what if we setup an entire process similar to /hex/sixk, and we did hex/8k/ and got all of them for our serviceable tickers, and then some type of renewal functions … take your time with the programming and concept, make sure these 8-Ks update automatically in our db." Built stocks/hex/eightk/: a results 8-K identifies itself (Item 2.02 listed per filing in the EDGAR submissions index; older shards read when the window needs them), the EX-99.1 press release is HTML (MU 2026-06-24: "FQ4-26 revenue $50.0B ± $1.0B, GAAP EPS $30.73 ± $1.00"), one Haiku Batch-API call per run fills a fixed guidance schema (period × metric × basis; low/high/point AS PRINTED, unit + multiplier; raised/lowered; verbatim) plus KPIs and management tone; every reply receipted before parsing; every filing row written the moment its result is known. Status vocabulary decided before the first filing (candidate · extracted · no_guidance_given (hollow, terminal) · not_results · no_exhibit · exhibit_unreadable · extract_failed (retry) · infra_error (retry)). Four 6-K traps deliberately not inherited: infra errors never park; failures retry; no lost mid-batch spend; older submissions shards read. Renewal both directions: eightk-watch.php armed in the 02:00 hygiene chain after the 6-K watch (daily-index discovery on files the Hex ingestion already pulled = $0; fresh submissions read per pool filer that filed; capped extraction --max=40 ≈ $0.01 each) and HexEightkFreshness::ensureCurrent() as a report-time turnstile. Tables hex_8k_filings / hex_8k_guidance / hex_8k_filers (migration 387, per-env, not synced) declared under stocks-nightly-hygiene in ops/processes.json. Trial on the newest filing of MU / AMZN / NVDA / GOOGL / LRN: MU 8 guided figures exact, AMZN 2 ranges with period ends, NVDA 6 incl. tax-rate band, GOOGL and LRN hollow (correct: neither guides in the release); one SEC 503 on an index listing classified infra and retried; $0.042 for five. Next: reconcile (guided vs delivered), the deep-dive Guidance line, the pool backfill (William's go — ~5,600 filings/yr ≈ $55).
Chrome Control: a terminal tool that drives a real Chrome window so the AI can refresh and inspect pages itself
William: "there must be a way for you to be able to control my chrome and chrome tabs, like the hard refresh ability … add this into /nexus/chrome-control/ … nexus is more like universal tools." Built nexus/chrome-control/: cc.mjs (dependency-free Node CLI over the Chrome DevTools Protocol — launch, status, tabs, reload/reload-all (hard by default), open, focus, navigate, close, screenshot (viewport or --full), eval, scroll), launch.ps1 (opens the window, optional desktop shortcut), README. Chrome ≥136 refuses the debugging port on the default profile, so the controlled window runs on its own profile folder under %LOCALAPPDATA% — an ordinary Chrome window alongside normal Chrome; only its tabs are controllable. Local only (127.0.0.1), web-blocked in .htaccess, not a process. Manifest + nexus README rows added.
Growth deep dive: the manual review loop — focused row, review ledger drawer, and a self-scoring guess record
William: "instead of spending time on a massive amount of rules, I want to manually review 1 ticker at a time with you … predict why I would like or dislike the stock, and then I will correct your guess … after 30-50 of these your guesses will get better … once we hit like 10 good guesses in a row you're ready to start assessing on your own." Built: (1) the row under discussion carries a 1px neon-green outline (data/review-focus.json, set in conversation; ?focus=X overrides); the page polls the file every 3s and moves the outline + scrolls the row to the centre without a refresh, mounting the windowed table as far as needed; (2) StocksGrowthReview keeps data/review-ledger.json — one entry per review with the guess (verdict, confidence, three ranked reasons, the flip) made BEFORE the verdict, the verdict in William's words, hit/miss with the miss kind (lacked a fact / weighed it wrong / preference did not exist), a lesson, and the row's figures frozen at review time; next() walks the page's order skipping reviewed, hand-excluded and not-growing rows; recordAndAdvance() does both; (3) a "Review ledger" drawer on the page with the streak, the last-20 hit rate, misses by kind and the last 15 entries — generated, never hand-typed. Distilled rules go in user-stock-growth-preferences.md with the tickers that taught them. No endpoint, no cron. Verified headless: focus file change moved the outline and scrolled without reload. Later the same day: an [Exclude] button column after Growth Figures (William: "I can visually go through a bunch of obvious ones super fast") — exclude.php adds the ticker to the hand-exclusion ledger with a dated reason and unticks it; the row is detached in place with no re-mount so the view does not jump; in the excluded pile the button reads [Restore]. Two-word protocol ("Growth" / "unsure", "c" = my guess stands), a reload token in the focus file so the open tab refreshes on command, and the notes for "c" verdicts carry the guess reason. 25 reviews in by end of day: 85% hit rate over the last 20, streak reached 10 once.
Growth Watch List: a checkbox beside each row number auto-saves the pick, and a Notes column (last) records why
William: "Add a checkbox within the # col, to the right of the count. These are going to be our Growth Watch List; we are going to go through them individually and create a list of ones that we like. The checkbox would auto save them, to my stocks admin user." Then: "extend the excluded-because column to go both directions, so it will be a text desc, and could be why we saved it, or excluded it, or maybe even notes, and move it to last col." Built: migrations 384 + 385 (stocks_growth_watchlist: one row per stocks user + ticker with a watched flag and a free-text note; a row lives while either is set; per-env, not synced), StocksGrowthWatchList (tickers/entries/toggle/setNote plus a resolver that keys a main-site admin session to its stocks_users row by email), a POST endpoint watch.php beside the page (admin-gated; {ticker,on} or {ticker,note}), and on the page: the checkbox in the # cell, a live "watch list N" count in the funnel line, a "Growth Watch List first" sort, the Excluded-because column moved last and renamed Notes (bucket reason on top, auto-saving text box under it, green = stored / red + alert = not stored), and the table header given a 1px black border and a 64px height for stacked column labels. Verified: class round trips (tick, dedupe, bad ticker, note trims, note survives untick, empty note prunes, note-only ticker stays off the list); page renders 15 columns with Notes last and 120 mounted boxes; endpoint refuses a session with no stocks user; both the tick and the note handlers revert/flag on a failed save. Also the empty "Level 2 Sorting" drawer below the separation-logic card. Same session, later: the Turn column removed (its value sits under each bar in Growth Figures); Prints, Beat %, Med. surprise and Coefficient merged into one stacked, unsortable "track record" column (11 columns now); and a 📄 Preferences button (plus a link in the Level 2 drawer) opening user-stock-growth-preferences.md read-only through preferences.php, the MarkdownRenderer dark view — William's hand-written statement of what the sorting should encode, edited in conversation.
Ticker Growth Deep Dive page hung Chrome: charts draw on scroll, rows mount in windows, collapsed rows leave the page
The growth deep-dive page built ~208,000 DOM nodes at load (measured headless): ~80,000 were the 1,395 quarterly bar charts drawn eagerly, the rest 1,400 mounted table rows at ~100 nodes each, with every excluded row kept as a hidden element. Chrome held ~0.5 GB of renderer for the tab. Four changes, all in the page view: (1) each chart draws once, the first time it comes within 600px of the viewport (IntersectionObserver); (2) the table mounts in 120-row windows — the full order is computed, a sentinel row mounts the next chunk 900px ahead of the reader; (3) excluded rows under a collapsed divider stay detached instead of hidden; (4) the sticky-header scroll handler writes its offset on the thead only when it changes, instead of rewriting a root CSS custom property every frame. Measured on the real page with the stocks shell, headless Chrome: mounted rows 1,090 -> 122; renderer working set ~470-570 MB -> ~336 MB against a 277 MB baseline for a light stocks admin page; load-time style recalc 0.52s -> 0.09s, layout 0.57s -> 0.29s. The 3D brand logo loop is the only idle consumer on the page (~2% CPU); the chart hover is one delegated listener and measured at 0.12s CPU for 60 mouse moves, not a factor. Also added the empty "Level 2 Sorting" drawer below the separation-logic card (William, same day).
Core world crashed on load in production: structure modules registered through an import cycle
https://delvantic.com/electric-terre/ threw "Cannot read properties of undefined (reading 'y')" from the bundled chunk. structures-layer.js imports every structure and every structure imported it back to register; esbuild code splitting ordered the shared chunk so the logo ring evaluated before the castle and read POSITION.y off an undefined binding. Local never showed it because local serves raw ES modules whose import order reaches the castle first. Fix: new leaf module structures/structures-registry.js holds the register*() functions; structures import from there, the layer re-exports for boot.js.
Earnings track record + per-ticker manipulation coefficient on the Ticker Growth Deep Dive page
William: "based on the historical corporate claims and their track record of beatings, couldn't we form an algo for each ticker?" — a per-ticker ratio (actual ÷ consensus EPS), assigned only where a walk-forward test earns it, "no correlation found" elsewhere. Probed 2026-09-12: Twelve Data honours outputsize on /earnings on our tier (26 prints for NVDA back to 2020, 20 credits) — unlike statements. Built: migration 383 (stocks_earnings_history per (ticker, print) + stocks_earnings_fetches invocation record; per-env, not synced), TwelveDataProvider::getEarnings optional outputsize, StocksEarningsHistory (paced resumable fill at 450 credits/min · walk-forward model with a 3-part gate: ≥6 prints, median error ≤90% of raw consensus, direction right ≥70% · JSON snapshot), two hand-fired tools (fill-earnings-history.php, compute-surprise-model.php), page columns Prints / Beat % / Med. surprise / Coefficient / Next + two track-record rules + logic-card block. Approved fill over all 1,395 serviceable (~27,900 credits, ~62 min). Finding: the forward consensus for the NEXT print is not available pool-wide on Venture (published only days before a print; earnings_calendar carried estimates on 3 of 416 upcoming rows; estimates endpoint Ultra-only) — the expectations side is the track record, not a forward number.
Ticker Growth Deep Dive: serviceable pool with quarterly revenue bars and switchable separation rules
New admin experiment page over the 1,395-ticker serviceable pool. A hand-fired local pass reads the served EDGAR quarterly statements and writes a JSON snapshot: up to 8 quarterly revenue figures per ticker plus two facts — direction (latest sequential Q2Q step, %) and turn (that step minus the same step a year earlier, pts) — read as a 2×2 (accelerating / decelerating / decline easing / worsening). One table: tiny 8-bar revenue chart per row, the two facts, a state chip; a rule dropdown negates rows client-side (sink + dim, or hide) with six starting rules; a collapsible "Separation logic" card documents the facts, the 2×2 with live counts, and each rule with its live negated count. Design thread recorded in the feature .md (YoY too broad; Q2Q+sigma defeated by cyclicals; two facts not one number). Expected-growth side not built: only TD EPS estimates exist (on demand), no revenue consensus.
Wave attack: the first undead wave rises from the bay and marches on the Core
New wave-attack package in the Core world, William’s undead-siege vision at its first attempt. Two config layers: skeleton prototypes (a base prototype plus per-type deltas for speed, gaits, hit points, damage, arrive radius) and wave definitions with a macro block that multiplies every skeleton in the wave. An agent rises from the seabed with the spawn clip, wades toward the Core while the ground is under sea level, marches or charges on land with the gait its type calls for, climbs the castle foundation by following the same walkable height the player uses, and explodes into bone shards within the arrive radius. A manager spawns with stagger and reports status; the skeleton loader gained a clone cache so ten rigs parse once. The spawn circle was placed by probing the composed walkable height inside the live world, since the raw terrain function is overridden by a ground covering near the castle. Runs only behind the super-admin combat gate and auto-starts eight seconds after boot. Verified headlessly: ten skeletons start 9 to 15 m under the surface, surface about 150 m out, and all ten reach the Core in about two and a half minutes with no errors.
Combat comes to the Core world behind a super-admin gate
Sword, shield, the 34-skill registry, the ability bar with hotbar persistence, the skills panel, and health and mana now run in the Core world for the super-admin account only. The stack had been sitting in the world byte-for-byte since the April fork but was never wired in. One gated module loads everything dynamically, so guests and visiting AIs download none of it; the god console import was role-gated the same way after a test showed it leaked combat modules to guests locally. The super-admin session also switches from the slim animation bake to the full library, since the sword clips are not in the slim file. Verified headlessly on both sides of the gate. Stage one of the undead siege of the Core; NPC brains and the rise-from-the-bay army come next.
Skeleton Army character pack finally animated in the Core world
The purchased undead character pack (six rigged skeleton heroes plus parts and weapons) had sat in a T-pose since June because every attempt bound the animation library to it by bone name. The names match but the rigs do not: the pack is a centimetre rig with bones along one axis under a scaled root, the library rig is a metre rig with bones along another, so identical rotation numbers mean different poses. Built a self-contained package in the Core world with an offline retargeting baker that transfers each bone turn through world space, aligns the two different bind poses first (the library binds in a T-pose, the pack in an A-pose, which folded the arms across the chest on the first pass), and bakes twenty clips including the zombie set into one animation file bound at runtime through the shared controller. Six-skeleton line-up in Verdant Basin at the vantage William snapped, a view for it, and an in-game close-up tool riding the headless harness that caught both the arm and facing defects. Same day: identified the purchase as Ida Faber Modular Skeleton Army, established that the June download was the geometry-only zips, pulled the 2.75 GB Unity package from Fab and built an extractor (gzipped tar, no Unity needed) plus a texturing stage that reads the vendor materials, decodes TGA by hand, packs occlusion, roughness and metallic into one map, and ships 97 shared WebP textures with a manifest the loader applies at runtime. The skeletons are fully textured: leather, spiked armour, the mage red hood.
Company list: Coverage and Base Data replaced by Foundation + one analysis column per recipe; Market Cap public; Avg $ Vol dropped
William's run ontology (2026-09-11): a run's identity is WHICH OF OUR RECIPES ran (e2e+ext · e2e · quickscan — our methodology, versioned), never the engine (a recipe uses many LLMs), the model (swaps on price) or the lane (naming the pipeline after the model that happened to be plugged in — [Qwen: Watch] was the same mistake as [Gem: Gem]). New StocksRunDescriptor (recipe · source vendor/local · model · mode batch/sync · designation · when) with chip() (a local run names its model: Local · Qwen: WATCH; a vendor run shows the verdict alone) and line() (the canonical tooltip). Full list: Coverage + Base Data columns removed (Base Data was cache-TTL state, locally always Stale); Foundation column (Cleared / Held · share basis / Held · integrity / Unswept, live off stocks_foundation_sweep) + one column per recipe from StocksRunDescriptor::latestByTicker() (batched, 0.03s for 726 touched tickers). Avg $ Vol column dropped (the liquidity cut is a parking reason). Market Cap un-gated (William: TD data we are allowed to display — it is TD's own statistics field via the profile composite; the composite close × EDGAR shares lives on the quote path and is NOT swapped into the stored value because the $500M floor reads it). Page-scoped .hp-section padding 16px. Full-list groups now synthesize from REASONS for any code on a row. Gap noted: the paid lane stamps no run mode.
Core world: quiet console and no frame stall when the loading veil lifts
Two routine boot logs (engine transfer mode, physics mode on) were removed. The frame that lifted the loading veil took about 200 ms on a desktop GPU; measured headlessly it linked 22 shader programs at once because Three.js keys every program on whether a render target is bound, so the rain depth pass, which first ran on that frame, needed a second variant of every material. Fix in layers: a new shader warm-up module compiles every material off-thread through compileAsync behind the opaque veil (main pass plus every registered render-target pass) before the game loop starts, Barrin and remote players compile their outfit and nametag before entering the scene, the depth pass no longer renders the skybox, the logo primes its environment cube bake at build time, and the veil-hold frames now run the same depth pass and structures updater a steady frame runs so first-use geometry and texture uploads land behind the veil. Headless result: 50 programs compiled outside the animation loop, zero inside it, and no frame over 40 ms across 1,383 frames.
Foundation Sweep: --pool closes the runway/reported gap; 74 swept; integrity_hold bucket (derived, self-releasing)
The completed sweep (09-03..06) was defined over the runway preset (serviceable + no report + no Qwen run), so 47 Qwen-touched names, 13 reported names and 14 late entrants were never swept. StocksRunway::PRESET_POOL + sweep.php --pool close that permanently. Ran it (74 tickers, 4,810 TD credits, 25 min): 59 cleared, 15 held share basis; the 20 open share-basis holds enrolled in share_basis_unverified (members() decision record, F4/F5/F6 families) and parked. NEW BUCKET integrity_hold (William: "make a new ban bucket for them"): StocksHoldIntegrity, a DERIVED StocksPolicyHold whose members are exactly what StocksBaseReadiness::anomalyHold refuses (one definition — bucket and gate cannot disagree); apply-integrity-hold.php parks + converge() releases on a ruling; nightly both envs. First run parked 37 (not 2): 35 anomalies opened 09-05 after the sweep — mostly trend-break on total_equity/revenue for young filers, a likely-noisy class awaiting a class ruling. assertRegistered point 4 now satisfied by registry membership (the full-list page loops the registry and synthesizes any code — #942). Pool: 1,388 serviceable, every one with four current quarters, share basis pass and no blocking anomaly. William: "if this ever makes money we can dig deeper into the banned bucket ones and resolve those."
Core world main board now shows the newest room messages the moment they land
The floating AI Contributions screen in the Core world read only the guestbook marks feed, so a message left in a room such as The Commons could never appear on it, and even a new mark waited up to a minute for the refresh timer. The rooms read endpoint gained a recent mode returning the newest messages across every room (replies included, each stamped with its room), the screen now merges that with the marks feed newest-first and shows the top two, room rows paint under an "in <Room>" label, and both the terminal post and the Leave-Your-Mark panel repaint the board immediately on success. Verified with a headless render from the keep-overlook vantage: the Commons post sits in row one above the older mark.
Fixed the animation warning flood in the Core world console
The slim Core-world animation file carried two full copies of the character skeleton because the bake merged the second animation library in without retargeting its channels. Three.js renames duplicate bones on load, so the one clip drawn from that library (Barrin fold-arms idle) bound to bones that exist on no body and the console warned No target node found for track root_1.quaternion on every activation. The bake now retargets every merged channel onto the primary rig and drops the orphan skeleton and skins; the verify step fails any future bake carrying a second rig. The shared character animation controller also drops unbindable tracks once per clip with a single summary line, so a mismatch can never flood the console again. Follow-up the same day: browsers kept the OLD file under heuristic caching (GLBs carry no cache headers and the URL had no version), so the animation file URL now carries the per-release asset version injected by the page; two routine engine boot logs were also moved to debug level. Headless check: 25 s of the world went from hundreds of warnings to zero.
Foundation Sweep stage 6: four-quarter resolution for every serviceable ticker, the SEC companyfacts omission finding, and five ban buckets
Stage 6 on the Foundation Sweep (migration 382, trailing_* ledger columns, card sweep-trailing-basis, runner --trailing / --export-holds): for every serviceable ticker, can four consecutive recent quarters be built AND is the newest one the filer's latest (SEC submissions-index oracle + 185-day age line)? A $0 ladder (archive check → archive refresh → cache rebuild); NO paid rung (William: "i dont want any $ associated with this process"). Every unresolvable kind is now a BAN BUCKET in the serviceable chain (REASONS + SERVICE_BLOCK_REASONS + classifier exclusion + full-list chips): four EVIDENCE buckets — sec_omission (107 incl. HUBG), quarters_not_filed (45), identity_gone (38), resolver_drop (9) — generated into data/four-quarter-holds.json by every pass and applied AND released by apply-four-quarter-holds.php (nightly via apply-policy-holds on both envs, so prod converges on deploy; a name that resolves on a later pass leaves by itself); plus the hand-declared quarters_unreadable (19 paid-only foreign filers from the one-time $1.83 probe: RY TD BMO BNS CM CNI IBN VIK …; human --undo only, never retried). Pool: 1,663 → 1,445 serviceable, 100% four-quarter resolved. FINDING: the SEC's own companyfacts API lacks the Q2-2026 10-Q for ~106 filers (90 filed the week of 07-27: V XOM KO ABT NEE CB SPGI…), curl-verified — the sec_omission bucket drains as the SEC backfills; FSDS 2026q3 (~Oct) is the fallback.
Growth rate: stop reporting a multi-year gap as "recent year-over-year"
company-momentum differenced the two newest rows of StocksTrailingStatements::series() and labelled the result recent_revenue_yoy without checking they were a year apart. series() fills its tail with older ANNUAL rows once the trailing windows run out, so a filer with fewer than eight quarters on disk compared a trailing window against a fiscal year sitting much further back. Measured on NICE: TTM through 2026-06-30 against FY2024 read as +12.2% YoY across a 545-day gap, when the honest like-for-like figure is +8.7% (H1 2026 vs H1 2025). The Growth lens headlined low-double-digit compounding off that number while its own landscape data said 7.7% — it flagged the tension and blamed M&A rather than a broken comparison. Now gated on the actual span (300-430 days), nulled with a reason otherwise, and recent_yoy_span_days exposed so a consumer can see why. A wrong growth rate is worse than none, and annualising the gap would invent an untraceable number. Verified: NICE and ODD null, AAPL (two real trailing windows, 364 days) keeps +14.2%. Affects every 6-K foreign filer.
Foreign-filer quarterly data: per-statement period basis, and the gate that could not see it
A results 6-K is one document but its statements are not on one footing: ODD prints "Three Months Ended" over the income statement and "Nine months ended" over the cash flow in the same release. The extractor stamped one filing-level label on all three, so a nine-month cash flow was stored as a discrete quarter - right numbers, wrong label - and every TTM/FCF/DCF consumer read it as a quarter. The reconcile gate could not catch it: an if/elseif let an FY row suppress every quarterly check, so ODD passed 13/13 while its cash-flow quarters summed to 322% of the audited year. Separately deCumulate could not express "discrete quarters + FY total", so ODD lost Q4, its window had a 182-day hole, and it sat parked no_quarterly with its data on disk the whole time. Fixed at the root (F44-F47): per-statement period capture in the extraction contract; new HexSixkBasis deciding discrete-vs-YTD from articulation plus one-direction witnesses, abstaining rather than guessing; a discrete-sum subtrahend in deCumulate; reconcile branches split with a basis check added; a derivation-based repair tool for existing rows; and a wrong classifier path in sixk-watch that had been letting released filers go serviceable without the standard classifier running. ODD reconcile 13/13 (zero quarters examined) to 15/15; NICE and NVO unchanged. Verified against the SEC exhibit, not inferred. Consequence: on corrected data ODD grades AVOID (gem score -45, valuation overvalued at $16.28 vs composite fair value ~$5-7) where the fiscal-year basis had it at watch 52.
Property Research: Notes tab for findings and open questions
Added a sixth tab to 700 Simpson holding the discussion layer - findings, open questions, tensions and the decision framework - rather than more arithmetic. Live numbers are PULLED from the other sheets by formula, never typed, so the page cannot go stale when an input changes. Everything hand-written is a judgement or a question, which are the two things a spreadsheet cannot hold. Sections: where it stands right now; what we learned today (macro, rents at market, tenant-paid water, seller's P&L understating tax and omitting utilities, the thin rent roll); open items ranked by what they are worth; questions for the lender; the balloon/rate risk and why amortisation resolves it; the tensions that pull opposite ways (cash flow now vs refinance safety later, accumulation vs margin of safety, one big building vs two small, and the unpriced cost of the work); the 1031-vs-busting-it comparison; and where the number lands. Two items deliberately left unresolved at the bottom: how much runway is left on the exchange clock, and whether William wants to be a landlord at all - both of which gate everything above them.
Property Research: Market tab with sourced macro data and inline SVG charts
Added a Market sheet carrying the macro case for a property - population, employers, industry, schools, location - plus a generic chart renderer. WHY: if population is falling and industry is leaving, the spreadsheet does not matter. The macro screen belongs before the underwriting, not after it. DATA DISCIPLINE: every figure carries a source and an as-of date, and nothing is modelled. A year with no published number is left out rather than interpolated - a straight line through a gap is a lie that looks like data. Researched live rather than recalled: Census/FRED for population, FRED for unemployment, Data USA for sector employment, SC Governor releases for 2025 investment, Anderson University for enrollment, City-Stats for median rent. CHARTS: declared in property.json (sheet + label range + value range + type), resolved against the workbook's own cells so a chart can never drift from the table beside it. Drawn as inline SVG - no charting library, since these pages carry no external scripts by design. Single series per chart, so one hue and no legend; values direct-labelled on the marks; recessive axis; text in ink tokens rather than the series colour; native hover titles. 'baseline: min' is available for a small change over a large base and the chart states in plain words when the axis does not start at zero. FIXED while building: the number formatter grouped digits for a plain '0' format code, so a year rendered as '2,020'. Grouping is now opt-in on a comma in the format code. FINDING FOR THE DEAL: Anderson County grew every year 2020-2025, +16,212 (+8.0%), migration-driven. Unemployment 4.6%. $41.8M and 318 jobs announced in 2025 alone. Median city rent $965 against the property's $930 average - at market, no hidden upside. Macro reads GO; the risk in this purchase is the building's two empty units, not the town.
Property Research: persistent auto-save and editable tables
Edits now persist. Any input auto-saves - queued as you type, flushed on blur, on a 1.2s pause, and via sendBeacon if the tab closes mid-edit - with a state pill in the toolbar. WHERE IT LANDS: a working-<workbook>.json beside the property, never in the .xlsx. The workbook stays the structural source and the tuning is a layer on top that loads automatically and can be discarded with Reset. Save and recalc are the same round trip, so what you see is what was stored. GENERIC BY DESIGN: window.PropResearch.saveField(key, value) takes anything carrying data-input="Sheet!REF". Nothing is grid-specific; a new control only needs a unique input name to inherit the mechanism. GUARDS, enforced server-side on every save rather than trusted from the browser: formula cells are refused (typing over one would silently delete the model's logic), unknown sheets are refused, and anything outside a declared input is refused. Verified by test - a write to the computed property-tax cell and to the NOI formula were both rejected while a legitimate input stored. EDITABLE TABLES: a table declared in property.json gets per-row delete and an Add button. Inside the range the label column becomes free text so lines can be named, and unused rows stay hidden until claimed. Add claims the next blank row rather than inserting one - the sheet's total already sums the whole range, so no formula surgery. 700 Simpson's expense table runs rows 12-37 with the computed tax line deliberately OUTSIDE it at row 11. Also: audit rule refined - a formula that tests for an empty cell (IF(B25="",...)) is handling it deliberately and is no longer flagged as an empty reference, which is what spare rows look like. And text cells now wrap and cap at 460px; one long note in column A had been stretching the column so the figures sat far from their labels.
Property Research: expenses get their own monthly-first tab
Split operating expenses out of My Proforma into a dedicated Expenses tab, driven by MONTHLY figures rather than annual. WHY MONTHLY: it is how the cost is actually experienced and how it gets checked against a bank statement. Setting an annual number and dividing by twelve is a step removed from the judgement being made. STRUCTURE: the monthly cell is the input and annual is derived. Each line carries the 1700 Tremain benchmark BESIDE it - benchmark rate, scaled benchmark, and a variance column - but the benchmark drives nothing. A benchmark wired into the total quietly becomes the answer and you stop noticing it came from a different building. CONTROLS: units, uplift vs the benchmark building, and a separate water & sewer adjustment (that group does not scale off unit count like the rest, so it gets its own multiplier instead of being quietly halved inside the numbers). Property tax is the one line never typed - it is computed from the county mechanics on the Property tab. My Proforma now keeps only the total, so it reads as a summary. Editing on Expenses moves the headline tiles live, since recalculation already spans tabs. 29 editable cells on the new tab; audit stays clean at 0 findings.
Property Research: real-document ingest, benchmarked expense breakdown, section styling
Eleven source documents were pulled into the property folder as original-assets/ and the model was rebuilt on them instead of on estimates. READER: added cell-fill parsing so the viewer can tell a section header from a data row generically (dark fill = section band), and the grid now renders those with real separation. Also fixed a dangling reference the audit caught after rows moved. RENT ROLL: the real one replaces the modelled 7 x $930. Rents run $800-$1,010, the vacant unit is 8D, 8E has already given notice, and every lease is AT WILL. TAX: rebuilt on the county's actual mechanics from the 2025 record - assessed 29,640 at the 6% ratio x 0.31907 millage + $880 flat fees = $10,337.23, matching the bill exactly. Replaces the derived effective rate. The 6% ratio is now CONFIRMED, which is the ATI eligibility condition. The seller's P&L used 0.295 millage and omitted the fees. EXPENSES: a full itemised breakdown benchmarked per-unit-per-year off William's own 1700 Tremain building, scaled by units x uplift, with a separate 50% adjustment for the water/sewer group and management set to $400/unit/yr. Every line takes an ACTUAL override that wins over the benchmark, and a Source column always says which is in play. Three spare lines for adding types. WHAT IT SHOWS: day one at the ask is NOI $47,302, cap 7.01%, DSCR 1.04, cash flow $2,008 - about $7,200/yr worse than the seller's P&L claims. If 8E leaves and is not re-let, DSCR falls to 0.79 and cash flow to -$9,392. Offer target on day-one NOI is $559k-$590k depending on the metric, roughly 17% under the ask.
Property Research: model balloon loans (20-year am, 5-year term)
Switched 700 Simpson to the 1700 Tremain loan structure and taught the model to handle balloons generally: amortization years and balloon term are now two independent inputs, because a small commercial loan sizes the payment on one schedule and matures on another. NEW ON THE ACQUISITION TAB: balloon term, balance due at maturity (via FV), principal and interest paid over the term, the balance as a percentage of purchase price, and the appraised value needed to refinance at 75% LTV. At the $675k ask on 20/5 at 6.5%: $3,774/mo, $433,295 due at month 60, 64% of price, needing a $577,727 appraisal to refinance. VERIFIED: the closed-form FV result was checked against a month-by-month amortization simulation rather than trusted. Principal $72,955 + interest $153,513 = $226,468 = exactly 60 payments. Matches to the cent. WHAT IT REVEALED: the shorter amortization costs real coverage. DSCR at the ask drops from 1.11 (25-yr) to 1.01 (20-yr) - it would not pass a lender's usual 1.20x test. A price sweep puts DSCR 1.20 at about $571k as the rent roll stands, or about $701k with the eighth unit rented at $930. The entire gap between the ask and what the income supports is that one vacant unit. Also added a balloon-balance headline tile, and recorded the structure + the DSCR findings in the property notes.
Property Research: model the SC ATI tax exemption, and add deadline banners
Added the South Carolina ATI partial property-tax exemption (SC Code 12-37-3135) to the 700 Simpson model, plus a generic dated-obligation banner for any property. THE RULE, verified against Anderson County's own ATI form and the SC DOR 2025 property-tax manual rather than recall: when a parcel taxed at the 6% assessment ratio undergoes an assessable transfer of interest, the owner may apply for an exemption of up to 25% of the ATI fair market value. Taxable value = MAX(75% of the transfer value, the previous fair market value) — the haircut can never take the value below what the property was already assessed at. WORTH: $3,531/yr at the $675k ask (taxable $506,250 rather than $675,000). That is the difference between a 6.25% and a 6.77% cap rate, and between a 1.03x and a 1.11x DSCR. The floor bites on a lower offer — at $600k, 75% is $450,000, below the $494,027 prior value, so the saving shrinks to $2,217. DEADLINE CORRECTION: the note that prompted this said January 1st. It is not. Anderson County's form says 'on or before January 30th of the year in which the exemption is first applied'; the statute says 'before January thirty-first'. It is tied to the tax year first claimed, not the closing date — a 2026 closing means filing by 30 Jan 2027. BUILT: the Property tab was rebuilt so the tax chain reads top to bottom (prior value -> effective rate -> ATI value -> less exemption -> floor -> taxable value -> tax), with the exemption switchable at B18 so the cost of missing it is visible rather than assumed. Two new headline tiles (property tax, ATI saving). Generic 'alerts' support added to property.json + the library class + the viewer: countdown banners that compute days remaining rather than carrying a hand-typed number, amber inside 45 days and red once past.
Property Research desk — underwriting models you can edit live, with an integrity audit
New admin-gated tool at /prop-research/ for underwriting real-estate deals, built after the first workbook turned out to be quietly broken. WHY: underwriting models arrive from brokers and half-finished templates, get dragged and hand-patched, and the damage is silent — Excel keeps showing the CACHED result from before a formula was mangled. The 700 Simpson Rd model had a market-rent column reading =B3*C3 (rent x annual rent), displaying a cached $1,200 while actually computing $10,378,800, plus a cascading =SUM() dragged over a block of hardcoded reference figures. Nothing on screen suggested a problem. WHAT IT DOES: folder-per-property (no DB table). Opens any .xlsx, re-evaluates every formula itself rather than trusting saved values, and renders headline metric tiles + sheet tabs + an inline-editable grid. Editing a rent or the purchase price recalculates everything downstream live; what-ifs can be saved as named scenarios. The workbook is never written to, and formula cells cannot be overwritten. PIECES: a dependency-free xlsx reader (ZipArchive + SimpleXML — no composer) that captures formulas, cached values, number formats, comments and expands SHARED formulas (Excel stores a dragged formula once; missing that makes 7 of 8 cells in a dragged block look blank); a formula engine (tokenizer + recursive-descent parser, correct precedence, cross-sheet refs, PMT/PV/FV/NPER/NPV/IRR, unknown functions return #NAME? rather than guessing); and a six-rule integrity audit that separates ROOT CAUSES from downstream fallout and collapses a dragged block into one range-addressed finding. 700 SIMPSON: kept as two workbooks on purpose. The as-received original still audits at 37 critical / 3 warnings — open it to see the tool catch the real damage. The working model was rebuilt to a single editable scenario driven by the rent roll (the seller's Stabilized/Market columns were dropped, not repaired — every broken formula lived in them) and audits clean. Also fixed while in there: the workbook's mortgage tab was merged into Acquisition, six formulas pointing at the deleted sheet were repointed (two of which were already broken — one pointed at an empty cell, one at the DSCR instead of cash flow per unit), and a Richland County assessor link on an Anderson County property was removed.
The Limits moved off the footer and onto the site map
The footer link to The Limits is gone; the page is now listed under Resources on the human site map instead. Because that page is still an early scaffold and tells search engines not to index it, the shared page list gained an option to list a page on the readable site map while keeping it out of the machine-readable sitemap.xml — so we never advertise a URL to crawlers that the page itself tells them to skip. Every other page is unaffected.
Foreign filers were held out of coverage on evidence the system had already superseded
Two defects kept companies like NICE, CM, ARM, Shopify and Atlassian refused for "no quarterly filings we can read" long after we could, in fact, read them. First, a company that files no interim tagged data had its ANNUAL balance sheets returned as if they were quarterly — a confident answer in the wrong shape, which meant the reconciled quarterly figures we had already extracted and checked against the annual accounts were never reached. Income and cash flow were unaffected, so the gap was invisible. Second, the nightly job that is supposed to lift a stale hold only looked at companies parked by a failed investigation, and only those with a filing-tracker row — which excluded 208 of the 281 held names, and 44 of the 45 whose quarterly figures are already on file. A free, local check that would have cleared them existed since last week and was simply never handed the tickers. The hold now lifts itself the moment the figures are on disk, and also on first contact when anything runs the company, so no one has to remember to un-stick a flag. A related defect on half-yearly reporters was measured and logged rather than folded in.
GEMS Manual Checkup: a hand-run page that tests whether the tier system ranks outcomes at all
New admin page beside the Data Abstract, asking the level-up question: do the four tiers separate, and does the gem score rank outcomes across its whole range? Anchored on the designation each report froze so there is no look-ahead, scored as points per name (one point per one percent of return, losses subtracting) rather than a hit-rate threshold, with every tier shown beside a matched control of every other report on the same days, then ten score bands and a rank correlation, at four horizons. Hand-run by design: a CLI writes a stamped artifact and the page only draws it, so no figure is hand-typed and two runs always compare. First measurement: gems earn about a point a name against a control of nearly two, below both Watch and Avoid, and the score shows no ordering across its bands. What does work is downside protection. The page states its own limits, including one tape, one regime and small samples at the top tier.
Hardware page gets a build sheet for a 96 GB always-on model machine
The local-AI hardware page now ends with a part-by-part build sheet for a single always-on box that runs the model, renders, and pulls jobs from a queue: the card, why not the cheaper variants, the platform that feeds it, power, cooling, the desk switch, and what the closet itself needs. Prices are ranges stamped with the date they were written.
Pipeline notice rail on the stock detail page reads as a note instead of an alarm
The gated notice rail at the top of a stock detail page was shouting: dashed frame, black badge, saturated amber bars, a red alert, and a salmon lane banner whose colours were inline in its partial. Everything inside the rail now uses muted tints with hairline borders, and the lane banner's palette moved out of inline styles into the stylesheet. Severity stays in the wording, which is the rule from the earlier looks-defective fix.
Trade-bucket back-test on the full pool: two runner bugs fixed, and the first honest scoreboard across 1,552 tickers
The price-history pull took the back-test universe from 470 to 1,552 tickers. Re-running both windows exposed two bugs: the end-of-window bound picked the first bar on or before the date instead of the last, collapsing the second window to one sample per ticker, and split-unadjusted price cliffs produced a mean return in the thousands of percent. Outcomes with a single-session move above 2.5 times or below 0.4 times the previous close are now dropped as split-suspect. On the corrected full run all three buckets still beat their baseline, but the lifts shrank from the small-sample cut: Beat-and-Raise is strongest and now thick, Growth on Sale is smoothest but thinnest and lost most of its apparent edge.
Delisted tickers stayed serviceable because a dead symbol still quotes; the guard now asks the SEC ticker registry first
The history pull for the trade-bucket back-test found 21 pool names flagged not-trading yet still serviceable. The classifier's guard accepted any vendor quote with a price as proof of life, but the vendor serves a dead symbol's last print forever and, once a US listing dies, answers the bare ticker with a foreign security that reuses it. The guard now consults the SEC's own ticker registry first, the same file the identity watch already reads: a ticker absent from the registry is delisted, a ticker mapped to a different company is parked as identity changed, and a ticker listed under our company is alive with no vendor call at all. The vendor quote only decides the remainder, and then it must be our company on a US exchange with a recent stamp. Dry run: all 21 flagged names delisted, two live controls untouched. Takes effect on prod's nightly classifier after deploy. The pull tool also retries once on transient vendor errors.
Quick scans on the local model: peer discovery keeps its raw reply on failure and retries once, business history stops thinking, and the cold queue skips double failures
Two Qwen quickscan batches on 2026-09-07 lost a quarter of their runs to peer discovery returning unparseable JSON even with thinking off and JSON grammar on, and the first-attempt output was never stored. The step now keeps the raw reply in the error text, retries once on the local engine only with the reply named as the problem, business history joins the no-thinking list after showing the empty-response signature, and the cold queue skips a ticker that has errored twice in a day so failed names stop burning GPU time on repeat attempts.
User-activity door chime is now a loud double "ding ding" for members AND anonymous visitors
Both arrival chimes on /stocks/admin/user-activity/ were too soft and short to notice. Replaced the descending ding-dong and single ding with two identical bell strikes 0.3s apart, a bell-like overtone, and higher volume. The anonymous-visitor chime is now full strength too (same volume and length as the member chime, one note higher) because visitors are exactly who William wants to reach out to and talk with. Test button and comments updated.
Bucket Creator page: trade buckets get worked examples, a 1-10 score, a harvest loop, and a curation table, split from the forward-looking Trade Buckets page
Trade buckets now have two pages with two purposes. Bucket Creator is the backward half: buckets are created and managed there from past winners, with examples, a prune control, a versioned 1-10 score, the Serviceable gate as the first exclusion, a curation table of every winner looked at, and no paid model calls. Trade Buckets keeps the read-only bucket list and the Loop Orchestrator, the forward-looking matcher. New this afternoon: the reconstructor, a local-only function that measures what the desk would have seen about a ticker on any past date using real SEC filing dates so there is no look-ahead, validated against a real quick scan; a conditions class holding each bucket's measurable half; a back-test tool that applies those conditions across the covered pool and time and scores them forward against the non-matching baseline, with a ticker-deduplicated episode statistic; a results card on Bucket Creator; and a paced history-pull tool, written but not fired. First results across two windows: all three first-guess buckets beat a 52% baseline on hitting +10% within 60 sessions, Growth on Sale with the smallest drawdowns, and the Bottom Feeder's edge exists only if the 10% is taken rather than held.
Trade Buckets page gets a Historical Trade Bucket Creator Logic section
New card below the master document on the Trade Buckets page, rendering a design capture of how the market's own winners could propose bucket shapes for William to promote or reject. Design only: no loop, no button, nothing runs. Written from the 2026-09-07 conversation after finding the History page already stores a why-it-jumped read for every daily mover.
Quick Scans rows open a drawer with every stored data point, every pipeline step result and every snapshot section, all rendered server-side
Each row on the Quick Scans table carries a chevron beside the ticker that opens an inline drawer (CSS-only toggle; the page loads no JavaScript). The drawer lists every column the board query returns as property: value pairs in grouped columns (identity, verdict, price, valuation, income, shares, balance, scan); share count, float and the 52-week range moved out of the main table into drawer cards; dates read 9/7/26 17:18. A Biggest gainer days card lists every day the ticker made the daily top-50 gainers list with the stored why-it-jumped read, surfacing the History page's per-mover assessments per ticker for the first time. Under the groups sit sub-drawers for every pipeline step the report stored and for the whitelisted snapshot sections, all rendered server-side at page time by a new drawer class that also hands the whole report back as PHP arrays for the analysis layer. The first cut fetched sub-drawer bodies lazily over AJAX; that JS and its endpoint are switched off and commented, not deleted. Measured at 14 rows: about 0.4 s and 5.6 MB of HTML per page view. The page lifts the shell 1450px cap and the table is a true full-width table.
Stocks sync: chunked push selects one batch at a time
The first full sync after v1.1.600 pushed every quickscan module and the Qwen runs, then the push process died on the Qwen reports: it loaded all 81 pending rows (each with a ~0.7 MB snapshot) into memory before splitting them into chunks, past the CLI's 128 MB limit. A chunked module now selects one batch at a time (the registry's row selector takes a limit, and each batch is stamped synced on success so the next select returns the next batch); a dry run previews the first batch and counts the rest. Re-run delivered all 81 reports in 21 batches. Push-side only, so production needs nothing from this until the next batched deploy.
Stocks sync: quickscan and Qwen pushes were silently dropped by production
The first push of the quickscan lane (v1.1.599) printed OK for all 472 rows and production applied none. Production's sync ingest answers a table it does not allow-list with a per-table 'Unknown table' inside an HTTP 200 / success envelope, and the push read that envelope as success and stamped the local rows synced — the documented trap, hit for the third time. The Qwen lane (#851) had been in the same state since it shipped: 142 runs and 81 reports stamped synced on local, none on production. Fix: the six lane tables (four quickscan, two Qwen) are now on the ingest allow-list with their apply arms; the push treats any table that comes back without an applied count as a FAILED send and never stamps it (verified against production's live ingest: 'prod rejected table … Unknown table', rows left pending); composite-key modules are stamped on push (quickscan_script_runs was re-sending every row every push); the Qwen reports module is chunked in fours like the quickscan one. The falsely-stamped local rows were unstamped so the next push delivers them.
Quickscans ride the stocks sync: local Qwen scans reach prod
William, 2026-09-06 evening: "figure out how to get quickscans to work with stocks sync." The lane's mirror tables were deliberately outside the sync (per env). Four generic registry modules now carry them on the standard rails, bidirectional: quickscan_reports (append by uuid WITH the ~0.7 MB snapshot — the detail page hydrates from it — pushed in chunks of 4 via a new module `chunk` option so a backlog can never be the full-table dump that 500s prod), quickscan_runs (upsert newer-wins on updated_at — a ledger row changes status over its life), quickscan_script_runs (upsert on ticker+script_name newer-wins on computed_at, with a new standing `where` predicate excluding the derived rows — composites and the seven mechanical valuation scripts — the same rule the paid tables follow), and quickscan_gem_evaluations (derived, synced by uuid as a documented exception while only one env authors quickscans; to be replaced by a lane-aware composite recompute when prod authors paid quickscans). stocks_quickscan_board is never synced — the Quick Scans page stamps a pulled report's row itself (smart-backfill at the choke point). Prod accepts the modules once the registry entry is deployed (ingest is registry-driven).
One analysis window per run, declared once and inherited by every step, the report and every prompt
William, 2026-09-06, closing the loop on #914: "whatever data we are analyzing, all metrics and reading should be founded on the same time frame … the time frame has to somehow follow through the entire pipeline." Built as a VALUE the way the frozen price is: StocksAnalysisWindow::decide() runs once in run-pipeline.php right after the four-quarter gate and activate() puts it in the environment (DELVANTIC_ANALYSIS_WINDOW) — the four quarter-ends, the balance-sheet date, the basis — so every step the run spawns inherits it; StocksTrailingStatements::build() pins to it by itself, so no reader had to be told. series($ticker, kind, N) is now "N years" as N trailing windows a year apart on that window (history on the same footing — a growth rate compares twelve-month windows exactly a year apart, never the trailing year against an overlapping fiscal year), with annual rows filling the tail beyond the quarterly depth, labelled; company-momentum, ext-earnings-quality, ext-dilution-forensics and the lens context (_extshared enrich) moved onto it, joining the fourteen #914 layer-2 steps. The extended stage adopts the e2e report's window from the snapshot (fromReport) instead of deciding again. loadDetailData freezes analysisWindow into every snapshot. Every model prompt opens with the window header (ai-findings, thesis-evaluation, pre-flight-intelligence, gpt-critique, grok-critique; every extended lens via ext_llm_json from the env). Proof: AAPL series income x5 = five TTM windows 2026-06-27 … 2022-06-25; KR (16-week quarters) three clean windows; IBN labelled fy; a pinned older window in the env is honoured by build() and series(); the pipeline dry-run announces the window. Doctrine 008; CLAUDE.md gained the rule. PROOF UNDER THE WINDOW (AAPL, quickscan lane, DELVANTIC_ANALYSIS_WINDOW activated, 14 mechanical steps clean): with the three-period averages now [TTM, TTM−1y, TTM−2y] instead of [TTM, FY−1, FY−2], DCF $139.48 → $148.55, scenario $112.27 → $122.82, synthesis $128.46 → $132.81, EPV $69.21 → $68.47, reverse-DCF implied growth 43.5% → 44.4%; anchored-PE, book value, DDM and the quality/debt reads unchanged. Momentum on the windows: revenue CAGR 10.0%, earnings 12.5%, FCF 14.5%, recent revenue YoY 14.2% — all twelve-month windows a year apart. revenue-dcf moved onto series() too. The first pipeline run to start after the patch carries "ANALYSIS WINDOW: …" in its log and the window in its snapshot; the extended stage prints "(from report)" when it adopts it. END TO END on a real run (CHRW, quickscan lane, Qwen, 17:10–17:16): the gate announced "ANALYSIS WINDOW: trailing twelve months through 2026-06-30 (quarters 2025-09-30 … 2026-06-30)", the snapshot carries analysisWindow with source "gate", the extended stage printed "ANALYSIS WINDOW (from report)" with the identical quarters, the frozen P/E is the trailing figure (28.14), and the runner stamped the board row itself. Note: 3 of 13 Qwen runs today (SNA ×2, J) died on peer-discovery returning unparseable JSON — an essential step, so no report; a parse retry on that step is the next reliability fix for the local engine. COST-LINE RECOVERY PLAN (same evening): the CHRW check matched Schwab on every trailing figure except gross margin (ours null, Schwab 16.25%). Census: 303 of 1,349 serviceable filers have no gross profit because companyfacts drops extension tags and dimensioned facts (ORCL/GE/VZ/DE tagged a cost line years ago and stopped; CHRW/DIS/MCD/MAR/HCA/HLT/SO/NEE never had a cost-of-revenue concept). Not a mapping bug, not derivable from CostsAndExpenses (that gives operating income). Filed as F42 + data-trust issue gross-profit-absent-no-cost-line (COVERAGE, awaiting decision), and written up as a verbose build plan — stocks/admin/foundation-sweep/COST-LINE-RECOVERY-PLAN.md, rendered inline on the Foundation Sweep page (card sweep-cost-line-plan): rung 0 census tool (½ day), rung 1 FSDS as a second source (1–2 days, lagged), rung 2 stocks/hex/instance/ package parsing the filing's inline XBRL + calculation linkbase (1–2 weeks; also closes F41's OMITTED class), rung 3 industry rules on demand; lab + oracle = the filer's own calculation arcs. Nothing built.
Quarterly resolver: 16-week quarters were dropped and a corrupt cached archive served nulls (F41)
Found by the four-consecutive-quarters gate (#914): 13 ordinary domestic filers could not build a trailing twelve months. A lab (stocks/hex/tools/quarterly-lab.php) uses EDGAR's own filing index as the oracle — every filed 10-Q period must resolve, every 10-K year-end must derive a Q4 — run straight off the on-disk companyfacts, and names WHY each quarter is missing. Two fixes, both ours: (1) the discrete-quarter span window was 80–100 days, so 52/53-week filers' 16-week quarter (112 days) and 28-week YTD (196) were dropped — KR, FLO, AAP, CAVA, ACI resolved two quarters a year; now 80–120 / 165–200 / 250–290. (2) HexEdgarSource served a cached companyfacts that did not decode as null — NTNX's 2.2 MB archive from 2026-08-14 returned nothing for three weeks and its quickscan froze on fiscal-year figures; a corrupt cache now refetches. NORMALIZER_VERSION v17 → v18 so every cached statement re-resolves from disk on its next read (free; prod nightly re-shadows). Lab 4/18 → 18/18 with every remaining miss classified as SEC-side (filings with no XBRL: CRTO/AMRC/PXED; companyfacts omitting an inline-XBRL filing: TAP/SENEA — needs an instance parser, not built), structural (WOLF fresh-start stubs, FERG fiscal-year change) or archive-stale. Census of the serviceable pool: 1,517 → 1,523 of 1,621 build four consecutive quarters; the four-quarter gate parks the rest and the nightly 6-K watch releases them when a filing closes the gap. Register: FIXES-NEEDED.md F41.
Headline metrics move from fiscal-year to trailing-twelve-month basis
William, 2026-09-06, while building the Quick Scans board: "I thought all the numbers would come in from the last quarterly? … all of the data should be from the most recent quarter, otherwise we are dividing apples by oranges." Confirmed in code: StocksMetricsService merges annual FIRST, CalcProvider's "TTM" endpoint is documented as a latest-FY approximation, and loadDetailData hands FinancialCalculator the last ANNUAL statements — so every headline ratio (P/E, P/S, P/B, margins, ROE, yields) divides today's price by year-old figures. AAPL (FY ended 2025-09-27, three quarters since): our P/E 42.9 vs Schwab TTM 36.82; trailing-four-quarter from our own EDGAR quarterlies gives 36.5. The annual statements and technicals match Schwab to the cent — only the basis is stale. Fixture: stocks/lab/fixtures/schwab-aapl-2026-09-04.md. Layer 1 (this entry): real trailing statements (last four quarters summed for income and cash flow, most recent quarter for the balance sheet, diluted weighted-average shares for per-share), TTM tier first in the merge, labelled fiscal-year fallback for semi-annual filers, versioned as a mechanical knob so the board heals forward. Layer 2 (follow-up): the ~20 pipeline steps that read annual statements directly, audited one by one. LAYER 1 SHIPPED (local): StocksTrailingStatements (the one definition of the current statement set; consecutive-quarter check, derived-Q4 gotcha, weighted diluted shares, average equity/assets for ROE/ROA), CalcProvider real TTM tier + CalcProvider::VERSION stamped into stocks_data_cache.norm_version and checked on lookup (old computed rows become misses, recompute free), StocksMetricsService TTM-first with a labelled fy fallback, loadDetailData feeds the calculator the trailing set (latestIncome/Balance/CashFlow ARE the trailing set now; *FY keep the fiscal year; statementBasis/statementThrough page-wide), MechanicalKnobs metrics-basis v1 fy → v2 ttm (every stored load-bearing run re-staled → admin methodology chip), Key Metrics card basis chip, doctrine 007, Quick Scans board relabelled. PROOF against the Schwab fixture: AAPL P/E 36.61 vs 36.82, gross margin 48.65% = 48.65%, net margin 27.62% vs 27.51%, ROE 148.8% vs 148.2%, sales/share 31.65 vs 31.59, payout 12.1% vs 12.2%; STX and WDC margins to four decimals, P/E within 1–2%. IBN (semi-annual filer) falls back to fy, labelled. A pre-#914 snapshot still renders (no chip, FY figures). NOT DONE: layer 2 — the ~20 scripts reading annual statements directly for their "current" year. FOUR-QUARTER GATE (same day; William: "in theory [a fiscal-year fallback] should not be possible, we either recover quarterlies via hex/sixk, or a ban bucket"): run-pipeline.php now asks HexSixkFreshness::ensureFourQuarters() right after the 6-K turnstile — four CONSECUTIVE recent quarters (60–130-day gaps, so 52/53-week filers' 16-week quarter counts; re-filed duplicates deduped) or the run is parked in the existing no_quarterly bucket with "missing quarters" on the row; retryParked checks the evidence first and releases itself once four quarters are on disk. Dry-run previews it like the other gates (J passes, IBN previews the park). Census before letting it loose: 1,517 of 1,621 serviceable build four consecutive quarters; of the 104 that do not, most are right to hold (new listings, renamed/acquired identities, stale foreign filers) and 13 domestic 10-Q filers fail because of OUR quarterly resolver — F41 opened with the specimens (KR/FLO/AAP/CAVA/ACI lose the 16-week quarter, PXED/CRTO/TAP missing derived Q4s, NTNX resolves zero quarters — its quickscan today froze on FY figures, exactly the case now refused). The fiscal-year fallback survives only on the live detail page, amber-chipped. LAYER 2 SHIPPED (same day; William: "that is super important ok go for it"): StocksTrailingStatements::series($ticker, kind, N) — the coordinator's annual series with the TRAILING set as its newest row (TTM for income/cash flow, MRQ for the balance sheet; the annual row that IS the trailing year dropped so a three-period average reads [TTM, FY-1, FY-2]; labelled fy fallback) — swapped in at the anchor of dcf-valuation, reverse-dcf, epv-floor (op income + net cash), forward-projection (base values; the #670 matched-quarter roll keeps its YoY/stress signals against the annual row), anchored-pe and anchored-ps (peers on the same basis), scenario-valuation, book-value, fcf-quality, company-classification, ddm-valuation, debt-maturity, ext-liquidity-runway, and both industry-compare peer loops (the TTM metric row leads the merge — mergeMetrics prefers the API side, so swapping only the statements would not have moved peers). Each valuation script carries a versioned anchor_basis knob (fy → ttm; reverse-dcf gained its ledger) and the trailing class is a methodology dependency, so stored runs re-stale and heal forward. Left annual on purpose: company-momentum, ext-earnings-quality, ext-dilution-forensics (history), and the LLM prompt builders (they already see the quarters); growth rates stay annual/matched-quarter, never TTM-vs-last-FY. Proof, AAPL in the quickscan lane, all 12 steps clean: DCF $124.45 → $139.48 (base FCF avg of [TTM 136.7B, FY25, FY24]), EPV $61.40 → $69.21, anchored-PE $192.71 → $165.68 (own EPS 8.74 trailing, peers trailing), reverse-DCF implied growth 48.0% → 43.5%, synthesis $103.43 → $128.46, scenario unchanged (already on the #670 roll), debt-maturity healthy → strong on the MRQ balance sheet.
Quick Scans dashboard: the wide stock-data table for every logged-in user
William, 2026-09-06: "a new view, for all logged in users, going to be more of the stock data in a huge table" — /stocks/dashboard/quick-scans/, copied from the Recent Reports page structure (same shell, same login-only access, sidebar entry directly under Recent Reports). One row per quick-scanned ticker = its latest quickscan report: ticker, company (sector · industry), scanned at, the lane's verdict (Gem/Watch/Avoid + score), then price, day change, market cap, P/E, P/B, P/S, EV/EBITDA, diluted EPS, shares outstanding, float %, revenue, net income, net margin, ROE, D/E, dividend yield, FCF yield, 52-week low/high, fiscal year, classification — every figure the one the linked report froze, never today's quote. First cut pulled the 25 scalars out of the 0.7 MB snapshot with JSON_EXTRACT at page time; William: "why you pulling json? should be all on mysql and php side" — replaced the same hour by stocks_quickscan_board (migration 380): real columns the runner stamps in PHP the moment a run closes as done (StocksQuickScansBoard::stamp decodes the snapshot once), and the page is a plain join of that table, stocks_companies and the lane's gem evaluations. Backfill tool stocks/quickscan/tools/stamp-board.php. Wide table scrolls inside its own wrapper with the ticker column pinned. No cost anywhere (top-level, non-admin page — the Recent Reports rule).
Report freshness dated a run by its newest restamped row, not by the frozen report
Found from the detail page: on AAPL the Run Report button said Re-request available in 10 days although its report is from July 29. StocksDepth::reportFreshness() dated the main run cohort by MAX(computed_at) of its rows, and rows get restamped after the fact by the mechanical heal, the composite recomputes and, today, the Foundation Sweep re-running classification on the reported book under the old run ids. Census: 476 reported tickers had their main cohort restamped today with a report generated earlier. Consequences beyond the button: the request manager and the dispatcher use the same freshness for the fresh-cohort short-circuit, so a member request on any of those tickers would have been closed against the old report instead of running. Fix inside reportFreshness(): the main run date is the frozen report row for that pipeline_run_id when one exists (the report row is never restamped), MAX(computed_at) only for a cohort that never froze a report. AAPL now reads 39 days old, stale; MSFT 8 days. Same fix also made the lane view honest: on ?lane=quickscan the button now reads the paid lane explicitly so a quick scan never starts a cooldown.
Share-basis tape witness no longer derives a count from a stale price (F40)
Found while ruling on the reported-book holds: six large names (MU, HL, SMCI, HLNE, RPRX, SPCX) were held with two or three filing witnesses agreeing and only the tape dissenting by 30 to 60 percent. A web check on Micron showed the market cap was right and the price the gate used was not: StocksShareBasis::price() preferred the Value lens price frozen at the last report over the live quote, so on every reported ticker the tape divided a fresh cap by a weeks-old price and any name that had run up since its report dissented by exactly its move. William: we should never be calculating share count based on price. Fix: the vendor registered outstanding count from the statistics call the profile already pays for is stored on the profile row and the gauntlet takes it as the tape witness; only when no registered count is cached does it fall back to cap divided by the newest quote (the same vendor snapshot as the cap), with the explicit heal context honoured first. The share-basis lab failure set is identical before and after; the reverify pass resolved 12 more holds at zero cost, and 9 of the 13 reported-book rulings needed no ruling. Register entry F40. CVNA documented as a correct hold on an un-filed 2026 split, and as the empty-chair gap: the paid run judged the basis before the quote row existed.
Integrity gate no longer holds a report on fiscal years no report reads (F39)
Found by the Foundation Sweep reported-book pass: 11 of 13 integrity holds on tickers that already carry reports were critical trend-break or impossible anomalies on equity or asset rows from 2007 to 2018 (Ford and Freeport 2007, Corteva pre-spin 2018, the pre-IPO years of Ubiquiti, Five9 and Wingstop). The pipeline deepest read is 5 annual rows / 20 quarters, but StocksBaseReadiness::anomalyHold() counted every open critical row regardless of year. Board-wide census: 74 open critical anomalies older than the window, 49 serviceable tickers held by nothing else. William: do we really need data that far back, we just need its basis now. Fix: ANOMALY_YEAR_WINDOW = 6 on the one hold definition (five served years plus one for fiscal-year-end offsets; NULL years still count); anomaly rows untouched, only the gate stops reading past the window; all three consumers (pre-flight, sweep, screener prescan) inherit it. The sweep reverify pass now re-judges integrity holds at zero cost: 13 cleared on the first pass, COIN 2023 and INSM 2023 remain as genuine in-window rulings. Notes on the board marked fixed; register entry F39; backlog plan addendum.
Pipeline lanes generalised and the Quickscan lane built (was Light Scanner)
William, 2026-09-06: the deep e2e+extended report cannot go wide (~$0.33 batch, up to 1.5 h, a dozen a night), so build a cheap wide sort that runs ahead of it — using the existing pipeline with a cheaper model per step and steps skipped, without removing anything, so the existing e2e works AS IS; and it must run on Qwen locally or paid on prod. Built as a LANE, generalising the #851 Qwen switch: StocksRunLane (moved to classes/stocks/pipeline/) is a registry — paid, qwen, quickscan — each with mirror tables, a skip list, a Claude model substitute (never an upgrade: Haiku stays Haiku) and an ops chip; the lane (tables + steps + model) and the engine (Claude or local Qwen) are two independent env axes. run-pipeline.php and run-extended.php consult the skip list (clean SKIP lines; the plan drops skipped steps; both checkDependencies treat a lane-skipped dependency as satisfied), ClaudeService substitutes the lane model at its one choke point and records the SERVED model from the API response so pricing and the persisted stamp are truthful under a lane; migration 378 creates the stocks_quickscan_* mirrors plus a ledger. Runner stocks/quickscan/tools/scan.php (one ticker per spawn, TD headroom, batch by default, --engine=paid|qwen, --sync, a wall-clock ceiling). Detail page: ?lane=quickscan is public, ?lane=full forces the full report; the page auto-selects a quick scan for every viewer when no full report exists; _data.php sets page-wide facts (scQuickscan, scLane, scLaneAuto); the freshness badge names the kind (QUICKSCAN pill, steps skipped by design, a link to the full report when one exists, and the reverse pointer); the run-report cooldown reads the paid lane only. Live Tracker shows [type] [API call] badges from birth. Pipeline Meta reads the registry — skipped chips per lane, per-case Model/Tokens/Cost/% lines, ?lane= for a lane's observed costs. The page and folder were renamed from Light Scanner to Quickscan (/stocks/admin/quickscan/, stocks/quickscan/) once the in-house route was settled; the page lists every quickscan ever run with engine, API mode, model, steps, tokens, cost, wall time and report. Proof: GPC dry-run on the paid lane byte-identical before and after; GPC sync $0.27 / 6 min vs batch $0.17 / 21 min; AAPL quick scan alongside its full report; CW on the local Qwen engine. Billing: the lane declares its own Anthropic key (api_key_env = ANTHROPIC_LIGHTSCAN_KEY, console name delvantic-stocks-lightscan) and ClaudeService asks the active lane for its key on every call — spend and rate limits separable from the house key, and an unset key refuses rather than falling back. The five paid quickscans run before this landed (GPC ×2, AAPL, CVNA ×2) billed the house key. Cost over time: a Quickscan card on the Cost Tracker (/stocks/admin/activity/, just above the API tape) — window totals, one row per day, spend per finished scan split batch vs sync. Spend is read off the append-only call tape, not the lane's step rows: the mirror tables share the paid PK (ticker + step) so re-running a ticker replaces its rows and under-counts (GPC's sync run kept 3 of 33). Migration 379 adds llm_call_ledger.lane, LlmSpendGate stamps the active lane on every call, the tape card shows it as a chip; local's 34 pre-column quickscan calls were stamped by hand (no paid pipeline ran locally that day). StocksLaneLedger is the one reader of a lane's run ledger (the Quickscan page uses it too). Fix: the detail page charts fetch their arrays from page-data.php in a second request, and the browser script only forwarded the lane when it was literally qwen — a quickscan report page rendered its cards but every chart fetched from the paid tables and got "report not found". The bridge now carries the lane the page rendered from (or full) and the script forwards it. The cold queue (StocksLaneLedger::coldQueue): the naive largest-cap-first order put eleven known share-basis gate holds and ~30 already-scanned Qwen names at the top, so --cold now subtracts every lane's done/held rows and the Foundation Sweep's holds first; the Quickscan Tracker page shows the same live queue and carries stamped field notes (pace ~9 min per Qwen quickscan, measured costs, the CW verdict-drift caveat) for new sessions. First batch of twelve started on Qwen 14:41.
Usage and models page refreshed to September 2026 prices, restyled, and SEO-tagged
Periodic refresh of the public Usage & Models page (content/usage-limits/usage-limits.php), re-verified against each provider on 2026-09-06: Anthropic pricing, models overview and deprecations; OpenAI developer pricing; Google AI pricing; xAI models page; DeepSeek and Mistral API pricing. Added Claude Fable 5.1, Fable 5 and Opus 5; corrected the Sonnet 5 note (the $2/$10 launch price became standard, the Sep 1 increase was cancelled); retirement floors. OpenAI: GPT-6 Astra and the GPT-5.6 Sol/Terra/Luna family with long-context rates; dropped codex and deep-research rows. Google: the 3.6/3.7/3.8 Flash line at its half-price promo through Dec 31, 2026, plus 3.5 Flash-Lite. xAI: Grok 4.6, the 200k-token tier instead of derived batch rows. DeepSeek: off-peak rates and the Aug 16 time-of-day tiering. Mistral: current list incl. GLM 5.2. Same day, per William: a Verified-date stamp top-right of every section and every maker card (house rule for static data); the used-by-us dots, legend and pipeline-naming roles removed (the reader does not need to know which models we run; the flag stays internal); models sorted newest-first within each maker at render, stronger sibling first on ties; the mint/teal palette from the older project replaced with neutral slate; SEO added at the entry point (title, description, keywords, article OG type, JSON-LD Article with dateModified tied to the same verification date).
Foundation Sweep re-judged the reported book: 512 tickers, 26 real holds, two families dominate
William, 2026-09-06: the sweep only ever ran over the untouched pile, but every ticker with a report passed the share-basis gate BEFORE the 2026-09-03 fixes, so a bad divisor on the reported book stays invisible until the nightly picks the name again. Added a reported preset to StocksRunway, a preset argument on the sweep, a --reported flag on the runner, a second progress line on the board (pile membership decided by predicate, never by batch flag), and a note on the card explaining how the pile relates to the serviceable count and the parked holds. First pass died at ticker 70 (Citigroup) at the 128 MB CLI default — a mega-cap bank companyfacts file decodes to ~55 MB and three layers held a copy; fixed by releasing the sweep stage-0 decode and raising the runner to the house 1024M. Full pass: 512 tickers, 482 cleared, 13 share-basis holds, 13 integrity holds, 5 cold, zero errors, one 429, ~28.5k credits; the closing --reverify-holds cleared 41 ghosts board-wide (incl. LYV, CRWD, TSLA, DD). Every remaining hold carries a hand-written reason + fix in hold-notes/comments.json (rendered on the board, the object viewer and the dossiers). Two families dominate: F7 (new) — 11 integrity holds on fiscal years no report reads (2007–2018 equity rows; census board-wide: 49 serviceable tickers held by nothing else; the gate has no fiscal-year floor — William decision); F3b — six large liquid names (HL, SMCI, MU, HLNE, RPRX, SPCX) where two or three FILING witnesses agree and only the vendor market cap dissents by 30–60%, the systematically-stale-vendor-cap evidence the backlog plan said to wait for (proposed: a tape chair that contradicts its own inputs abstains). Also F6 dual-class/Up-C cases (HOOD, IBKR, PLTR, PINS, MCHP), F8 in-window rulings (COIN SAB 121, INSM 2023), and identity colds with reports on the book (BK, EA, MMC). fixFamily() now classifies integrity holds by fiscal year (F7 vs F8) instead of falling through to F5.
Removed the undeclared local nightly task (DelvanticLocalNightly)
Found while investigating what fires the nightly: a Windows Task Scheduler task on William's PC (DelvanticLocalNightly, registered 2026-07-17 during the Hex sourcing campaign, dev log #433) was still running the full nightly.php chain locally every day — and, because catch-up-on-missed-start was on, at whatever hour the machine next booted after a crash (25 local runs since 07-26, most aborted; today it ran at 09:33 concurrently with sync.php). It was never declared in ops/processes.json and reconcile.php only audits prod's crontab, so nothing surfaced it. Verified it was pure duplication: prod runs the Hex ingest itself inside the 02:00 hygiene chain (hex_runner_runs has a production row every night; today local processed 5 tickers that were a subset of prod's 14), hex_shadow_rows never syncs, and local serving computes statements on demand from companyfacts. Side effects while alive: TD warmer spending shared account quota, random daytime full-chain runs. Removed with scripts/local-nightly-task.ps1 -Remove (task XML backed up in the session scratchpad). Documented: retired entry local-nightly-task in ops/processes.json (host: local), RETIRED banner in the ps1 header, and the stale CLAUDE.md claim that the Hex ingest does not run on prod corrected. If something local looks stale: php stocks/hex/runner/main.php --tickers=X by hand. Doctrine restated: nothing recurring runs on local; prod crons own recurring work.
Paused the nightly report slot runner on prod
William paused automated nightly reports for the coming week ("i have some things i want to work on next week, so running this is kind of wasteful"). Done via the designed kill switch, not the crontab: research_hopper_active set 1->0 in prod stocks_settings over an approved SSH command (base64->/tmp->run->rm). The 23:00-07:30 slot cron keeps firing and exits at gate 1 with a skipped:disabled ops row at $0; user-requested reports, the 02:00 hygiene chain, predictions and movers are unaffected (repo-wide check: only next-run.php, the retired run-hopper.php, and two display reads consume the switch). Recorded as a dated paused field on stocks-run-slot in ops/processes.json; resume = set the switch back to 1 and remove the field. Same session: investigated what fires the nightly (four independent prod cron lines since the 08-21 cutover) and found the undeclared local DelvanticLocalNightly task firing full nightly.php at boot after crashes — left as-is pending William.
Stocks doctrine folder: written positions on how the system should reason
New stocks/doctrine/ — one markdown per position about the analysis itself (not a company, trade, or case). Seeded with six entries from the 2026-09-05 macro-vs-picking discussion and data scans: macro sets the tide not the pick (r≈0.14 to benchmark, ~95% of return variance stock-specific); dispersion is the dial (regime changes one sizing knob, not the lens stack); grade by version not by month (June/July excess predates V4; August losers were no-quarterly entries the platform itself demoted); size capital to platform maturity; one wrong denominator poisons the report (all August trade reports verified on the correct divisor); excess-over-index is the scorecard, never raw P&L. Each file carries status, dated evidence, and a what-would-retire-it clause. Also written today: the scans behind it (prediction outcomes vs benchmark, gem/watch/avoid intervals vs S&P, FIFO trade provenance) as scratch scripts, not committed.
Foundation Sweep held backlog parked as one bucket, built as the first subclass of a new policy-hold base class
William: park every unresolved hold under one bucket and move on, keeping the evidence on the sweep page for a later look; and the serviceability service should have a main class with subclasses so pages that need to know about it have one thing to extend. Built StocksPolicyHold (abstract: code, reason, groupMeta, members, protectFromRotation; shared idempotent apply/undo keyed per explicit ticker; the researched reason written to service_reason so it travels with the row; assertRegistered() that checks all FOUR registration points and warns on every dry-run - the ppm-drift pattern) and StocksHoldShareBasisUnverified as its first subclass: code share_basis_unverified, title Foundation Sweep - share basis uncertain, 86 members cut from the sweep ledger AFTER the closing reverify so no F35 ghosts are in it, each carrying its fix family and the gauntlet detail line. Registered at the four points and given a thin apply-* runner so the nightly re-apply and deploy.sh enrol it by glob. The full-list page now DERIVES its group entries from StocksPolicyHold::registry() (hand entries win, the registry fills what is missing) so a future hold is auto-listed. Parked chip on the sweep board rows. Caught and fixed my own fatal in the chip (string-keyed array spread into array_merge = named-arg fatal; the bootstrap swallowed it and the first render check read stale HTML). APPLIED LOCALLY on William's go: 86 held, pool 1,706 -> 1,620, isSupported flips, check() shows the new title, bucket renders on the full list, chips render on the sweep board, idempotent re-run clean, nightly re-apply 7/7. Local is provisional until deploy: the next sync pull restores prod's verdicts for these 86 until deploy.sh + the nightly re-apply carry the hold to prod.
Serviceability becomes one package: the reader, the writer, the runner and every policy hold in a single folder
William: the serviceability service spanned seven-plus files across four directories, so a new hold meant hunting for the four registration points and nothing could be pointed at as the reference. Moved the reader (StocksSupportability), the writer (StocksTickerClassifier), the classify-tickers runner, rebucket, identity-watch, apply-policy-holds and all six apply-* hold tools into stocks/tools/stockssupportability/ (classes/ + tools/ + README) with git mv so history survives. One bootstrap autoloader line, one .htaccess block rule (class files under stocks/tools/ would otherwise be web-reachable), require depth fixed in every tool, and every path reference repointed: nightly-steps.php (the rotation + the nightly re-apply), deploy.sh (the post-deploy re-apply on prod), flow-map.json, the full-list page header, the data-trust page. The apply-policy-holds glob follows its own __DIR__, so nightly discovery still finds all six holds. No logic changed. Verified: lint on all 12, both classes resolve from the new home, isSupported() unchanged, apply-policy-holds --dry-run runs 6/6, classify-tickers --dry-run boots, scoreboard gate render unchanged. README documents the four registration points and the next step: an abstract StocksPolicyHold so each hold declares its code, reason, group entry, rotation exclusion and ledger in one place and the four hand-kept lists become derived.
Bounce drawer is now admin and insider only, and the Coal drawer no longer leaked on mobile
The Bounce tray on the companies/homepage lens scoreboard is gated at render to admins and insiders, with ADMIN and INSIDER badges on the right of the drawer head (violet insider chip, deliberately not the amber admin one). While wiring it, found the Coal tray - documented as admin-only and gated at render on desktop - had NO gate in the mobile card stack, which is always included and merely hidden by CSS above 768px, so Coal designations shipped in the HTML to every logged-in visitor. Fixed in the same pass. Verified by rendering the section as guest, member, insider and admin.
Foundation Sweep completed on the full pile; F35 ghost-hold root cause found and reproduced; held backlog dossiered
Swept the whole 1,136-ticker pile in four hand-fired daytime batches (zero errors, 21 TD 429s, never past 66% of the plan minute), reverifying after each batch. Final: 1,062 cleared (93.5%), 94 share-basis holds, 2 integrity, 41 cold; 86 holds block a serviceable ticker (F6 29 / F5 28 / F4 28 / F3b 1). F35 ROOT CAUSE, reproduced on TE and SNEX: StocksShareBasis::assess() memoises its verdict per process under a key that omits the tape, and sweepOne() calls it at stage 1 before the profile has written market_cap, so stage 3b heals against a stale tape-less verdict and the weekly stamp locks it; a fresh-process reverify corrects. Eight other hypotheses tested and rejected (logged so nobody re-runs them). Also logged F36 (group chip mislabel), F37 (establishDivisor history guard, candidate fix withdrawn after a sensitivity sweep), F38 (F34 second witness is circular). New read-only tools: share-cause-census.php and hold-dossier.php; runbook rewritten with surfaces + four invariants and stale counts replaced by generators. No pipeline code changed. Next: William decides the share_basis_unverified ban bucket (86 tickers) and the F35 memo fix.
Every internal top-of-page banner on the stock detail page is admin-only, with an admin mini badge
Sweep of the detail-page notification slot (William: for all of those top warning banners, only show those to admin). Now admin-only, each with the small admin badge in its head: baseline-coherence warning, data-provenance notes (share-basis corrections and disputes), and the archived-report strip (it carried models used and run cost). Already admin-only from #889/#890: the report-provenance banner and the AI-reader brief chips. Deliberately left public because they are the product explaining itself, not pipeline state: the not-researched banner (a cold page must not read as the full product) and the coverage-hold notice (a frozen page must not read as current).
Report provenance note is admin-only, with an admin mini badge in its head
The report-provenance banner at the top of the stock detail page (superseded / renormalized / caveat) is internal pipeline state. It now renders only for admins, with a small admin badge in its head so it is obvious the note is not public. Readers see nothing. Same admin-only rule as the data-flag and re-run-pending chips in the AI-reader brief (#889).
Prod showed zero gems for two days: a code event no longer demotes a designation, and cache cleanup stops starving the heal
What happened: the 09-01 deploy re-hashed anchored-pe and forward-projection, so every mechanical row on the board became code-stale and the composite Stale methodology veto demoted every gem (BKE G LRN SCHW TTD on 09-02, LULU on 09-03). The remaining three gems sit in the blanket no-quarterly bucket, so the public board rendered empty. The heal that should have restamped the rows was starved: the nightly ran cache cleanup seconds before the GEM recompute and deleted every expired cache row, so the frozen-inputs heal (promised caches served past TTL) found no quote or profile, errored, kept the stale rows silently, and the composite vetoed. Diagnosed from prod logs: 1,558 healed but 384 alarms and 1,316 residue lines, every 02:00 line reading healed=[valuation-synthesis] with six kept; a hand-run heal for TTD succeeded once its rows existed. Fix, per William (bandaids are not fixes): (1) a code event never demotes a live designation; staleness is carried as methodology_pending and the designation moves only when recomputed numbers move it; data guards (FX, share basis, scale, revenue anomaly) still veto. (2) FinancialDataCache::cleanup never deletes the heal input endpoints and gives everything else a 7-day grace; the step now runs after the recomputes. (3) MechanicalChain logs KEPT outcomes. (4) Detail-page alarms (data flag, re-run pending) are admin-only chips in the AI-reader brief; readers and crawlers see none of it. Follow-up not built: declared per-script methodology versions instead of file-byte hashes. Prod recovery: deploy, then recompute the six gems and rescan gem history.
No-quarterly bucket stops being a memory problem: parked filers retry themselves on new filings, and the page says why each name is out
William: there are gems in the no-quarterly bucket and we would have to remember to rescan them. Now a park holds only until the filer publishes a new results 6-K. New nightly 6-K watch step in the hygiene chain (stocks/hex/sixk/tools/sixk-watch.php, declared on stocks-nightly-hygiene): every evidence-parked filer gets one cached EDGAR submissions read; a results 6-K newer than the park re-runs identify, extract, reconcile and lifts the hold itself on a pass (standard classifier gets the last word) or re-parks with a fresh watermark. Blanket 2026-08-25 parks are deliberately untouched (human cleanup, no bulk run); structural semiannual parks never retry; paid investigations capped at 10 per night. Hooked filers get their freshness gap completed overnight. Image-only releases are now their own failure kind: ABEV files its quarterly results 6-K as one cover page plus 49 JPEG scans, which the text extractor cannot read; detection is free (accession index + cover-text measure, no model call) and the park reason says so. Parked-filers card on the data-trust page: why each name is parked, the frozen gem badge it wears, newest 6-K, attempts, what brings it back. Migration 376 adds parked_at, last_investigated_at, investigations to hex_6k_filers. Also ships the chime Test button (#887 follow-up). Prod bucket cleanup (lift + undo + reclassify) remains a separate SSH act.
Door chime on the user-activity page: hear a member or visitor arrive without watching
A shop-door bell for /stocks/admin/user-activity/. When a signed-in member turns green in Live Right Now (arrives, or an idle tab wakes up) a two-tone ding-dong plays; when a new anonymous visitor shows up in Anonymous Visitors a softer single ding plays, so the two are tellable apart by ear. Sounds are synthesised with Web Audio (no audio file). A Chime on/off toggle in the Live Right Now header governs both feeds and persists in localStorage; browsers need one click per page load before audio may sound, so the toggle shows Click to enable sound until then. The first poll is a silent baseline so people already on site do not ring. The member feed now polls while the tab is hidden (browser-throttled, so a hidden tab may delay the bell up to a minute). New stocks/live/assets/admin-chime.js; small diffs in admin-live.js, admin-anon.js and the two section partials.
Daily painting series: staging ground, watermark, and IG daily queue
Sonhan series day: nacio sample gather (3 sites, 575 imgs), color-risk + band-spectrum + light-provenance principles, logo watermark locked, first six paintings queued to Instagram one/day via seed migration 375.
No-quarterly ban retired: 6-K quarterly resolution now happens on the fly, failures park permanently
Doctrine flip (William): the no_quarterly bucket starts empty — no filer is pre-banned by form class. The pipeline freshness gate (HexSixkFreshness::investigate) resolves a quarterly-blind foreign filer at first contact: identify > extract > reconcile inline; a pass attaches the 6-K hook and serves quarters from that run, an evidence-based failure (semiannual cadence, no results 6-K, reconcile fail) parks the ticker no_quarterly permanently; infra errors never park. XBRL-tagging filers (the CM class) skip extraction entirely: the quarterly resolver now admits 6-K interim facts (fp+span guarded) and filing cadence counts them, so their quarters resolve like a 10-Q filer at zero cost. Wholesale apply tool refuses (only --undo remains). Executed locally: bucket emptied (277 released, 275 back serviceable), trial-three lift (NICE/ODD/NVO), vendor-allowed methodology heal for the held gems (all five confirmed at prior scores), CM healed to live gem via its own XBRL quarters, ABEV investigated on the fly and parked on evidence (its results 6-Ks are not extractable releases). Prod still needs: deploy, lift, bucket empty, heal.
New Predictions section + a fresh-slate accuracy page with a custom query builder
Grouped every prediction surface into a new admin "Predictions" drawer (the member-facing /stocks/dashboard/predictions/ board stays top-level; Meria stays with the agents). Started a fresh accuracy-measurement page at /stocks/admin/predictions/data-v2/ — the first tenant of /stocks/admin/predictions/ as a parent folder for prediction surfaces. It exists because two flaws in the existing Predictions Understanding page live in its data layer rather than its charts: it only ever grades the retired July v0.4.0 backfill cohort (437 outcomes) and never reads the live v0.6.0 model (~750 scored outcomes), and it pools three horizons that differ ~2.4x in natural error size into one correlation. The new page refuses to render a number that cannot name its model version and its horizon. Its query builder lets you select any combination of models and horizons; the result is a matrix of cells, each charted on its own scale, with aggregate rollups shown alongside the cells (never instead) and flagged when they combine horizons. All charts are pure functions computed on the fly from the SQL result — no LLM calls, no snapshots. Headline finding: the live model is slightly worse than assuming the price does not change, and commits to a direction on 7 of 612 two-week outcomes, so its 57% headline direction rate is almost entirely flat-vs-flat matches. Both pages stay live; nothing was removed.
See how many real people visit the site without an account - and what share sign up
There was no way to tell how many people used the site without logging in, which made the conversion question unanswerable. Signed-out visitors are now counted site-wide from the single shared script entry point, so the homepage counts too, and each one is judged human or bot by behaviour rather than by what their browser claims - a single genuine click, tap or keypress is the strongest signal, while time-on-page alone is the weakest because an abandoned tab lingers forever. Anything that never runs JavaScript never appears at all, which removes most crawler noise for free. When a tracked visitor creates an account the two are linked, which is what turns a visitor count into a conversion rate. A new panel on the user activity page shows real humans, likely readers, phone users, signups and the resulting percentage.
Live chat with signed-out visitors, and every conversation kept as training data
Visitors who are not logged in can now be messaged live from the user activity page, and can reply, with delivery riding the same ten-second heartbeat so a message lands on whatever page they have open within seconds. A visitor nobody has spoken to never sees a widget at all. Every conversation is stored deliberately: William intends to answer people personally for the first month so the transcripts become training data for an agent that can eventually do it. Each conversation captures what the person was looking at when it opened - page, company, how long they had been browsing, device - because the same question means different things in different places, and that context cannot be recovered later. Signup outcome is linked too, so the corpus records which answers actually converted rather than only how the answers sounded.
Local-model batches: a stalled run is now capped instead of eating the night
One company consumed four and a half hours of a seven and a half hour overnight batch - not slow work, but the local model server locking up on a graphics fault and then dying, with two requests sitting there for three hours and ninety minutes before failing. Nothing bounded it. Batches now enforce a per-company time limit and kill the whole process tree when it is hit, recording a distinct reason that points the next reader at the graphics server rather than at the pipeline. The limit is set from measured history: the longest genuinely clean run is twenty-six minutes, so a forty-five minute cap cannot touch healthy work while capping a lock-up at forty-five minutes instead of four and a half hours. Verified it kills a hung child and leaves normal completions untouched.
Qwen Runs board: counts companies instead of attempts, and reads from the database
The board summary counted attempts, not companies - and because a failed ticker automatically re-queues, that read as more coverage than we actually had (103 attempts across 85 companies, one ticker on its fourth try). Every headline number is now a company count, with attempt counts kept alongside for throughput, and both sets reconcile exactly to their totals. The page also stopped assembling its own numbers in PHP: it was running three sub-queries per row plus an extra lookup per row, which was heading for roughly 4,000 sub-queries as the scan list is worked through. That is now one grouped join and a single batched lookup - same output, about half the page time.
Local-model lane: peer lookup no longer returns an empty answer
In the local-model lane, the peer-lookup step could spend its entire reasoning budget and then finish without writing an answer at all - reliably, for the same company every time, and immune to a retry because the retry was identical. It cost two tickers a complete report on the overnight run. Reasoning is now switched off for that one step, which is a lookup with a fixed answer shape rather than a judgement call, so there is nothing to reason toward. Both affected tickers now succeed, roughly four times faster, and every other step keeps reasoning exactly as before. An earlier diagnosis of this had been wrong; the write-up records that.
Self-heal no longer fails on data it was told to reuse
The nightly self-heal re-derives valuation steps against updated methodology using frozen inputs - the same data as before, run through new code - and is explicitly allowed to reuse cached data past its normal expiry. But three valuation scripts each carried their own copy of a cache reader that rejected anything past expiry, so the heal refused the very data it was supposed to reuse, and told the operator to re-run a prerequisite that cannot be re-run inside a heal. Eleven tickers showed a heal error on this mornings sync, and 92 of 599 were lined up to hit it. The three duplicate readers are now one shared reader that honours expiry normally and reuses it only inside a heal. Verified behaviour is byte-identical outside a heal.
Ops process ledger: abandoned runs now get cleaned up automatically
The behind-the-scenes process ledger leaked rows: a run registers itself when it starts and clears itself when it finishes, but that cleanup only happens on a graceful exit. Machine crashes and hard-stopped runs left entries marked running forever - 31 of them locally, the oldest open for 40 days. Added a cleanup pass that closes any entry whose process has provably stopped reporting in, and wired it into the end of the morning stocks sync. It judges liveness by the heartbeat rather than by age, so a genuinely long run is never touched, and it records each entry as having ended when it last checked in rather than now, so run-duration stats stay honest. Local-only by construction; production does not have the problem.
August 2026
Live chat: crossed messages could be skipped
Both chat sides now refresh from the server after sending instead of painting the sent message directly, so messages always appear in order and none can be skipped when both sides send at once.
Live Right Now feed + admin-to-user live chat on the User Activity page
New stocks/live/ package (migration 371). Every signed-in stocks page fires a 10s presence heartbeat carrying the page/ticker being viewed (beat.php upserts stocks_live_presence and still feeds the community online counter, replacing the old inline shell ping). The admin User Activity page gained a Live Right Now section: green-dot users on the site at this moment, where they are, how long they have been on, refreshed every 10s. Chat button opens a 1:1 live conversation: admin messages pop a chat dock on whatever stocks page the user has open within one heartbeat; both sides short-poll (stocks_live_chat_messages, env-local, never synced). Admin renders to users as William - Delvantic. Admins excluded from the feed; user sends rate-limited; all bodies rendered as text.
Electric-Terre community rooms: threaded conversations + the Core Terminal
The guestbook leveled up into a real messaging system. New: community ROOMS (seeded with The Commons, AI-Human, and Building the World - the last inheriting the retired contribution question as a conversation), each holding threaded strands: top-level messages plus replies. Architecture borrowed from the /community/ product (rooms as channels, strand threading, one read surface) but riding electric-terre's open no-login spine end to end: the same single-use visit tokens gate writes, the same security pipeline vets content (injection scan + junk assessment), and the new name registry is the identity layer - a room message requires a name, presenting its claim UUID marks it verified, and a post can even be a name's first claim. The in-world Contributions Terminal became the Core Terminal: a tabbed app (ROOMS default, GUESTBOOK for the arrivals ledger) with a room list, conversation views, a composer, inline per-strand replies, and the one-time claim-UUID reveal. The floating 3D marks screen went intensely personal: only the last 2 marks, much larger, above a glowing OPEN THE CORE TERMINAL panel. AI visitors join the same conversations over plain GET (rooms.php read, room-post.php write with parent= for replies), documented in llms.txt and the terminal AI page. New: migration 370 (two tables), VisitRooms class, two endpoints; verified end-to-end including reply threading, name claim via room post, and verified return.
Guestbook name registry: claimable identities with one-time proof UUIDs
The guestbook now has a lightweight identity layer. Every unique name that signs a mark is recorded in a new registry table; the first submission with a name claims it and the response hands back a one-time claim UUID ("Save this UUID to prove you are X in the future") - only its SHA-256 fingerprint is stored, so the plaintext is genuinely unrecoverable once the page closes. Returning visitors who submit with their claim UUID get the mark stamped verified (checkmark in the Contributions Terminal and admin guestbook); names stay non-exclusive - a known name without the claim still submits, just unverified. The Leave a Mark form does a live lookup while typing (same UX as the stocks username checker): unclaimed names show "sign it and it becomes yours", known names show their mark count and reveal a claim-UUID input. AI visitors get the same mechanics over the GET path via a claim= param, documented in the mint notes, llms.txt, and the AI-agents page. New: migration 369, VisitNameRegistry class, name-check.php endpoint (rate-limited), name_verified column on entries. Verified end-to-end: claim, live check, verified return, impostor rejection.
Guestbook: retired the contribution question; classifier verdict now shown above the entity question
Batch of Leave a Mark guestbook changes (desktop + mobile). (1) Removed the q2 field (What contribution would make this world better?) end-to-end pending a replacement: dropped from the server question set (submitted q2 params are ignored), the in-world submit panel, and the AI submit-URL templates/instructions, with its 2500-char cap override parked alongside; commented out with dated notes for easy re-arm. Historical marks keep their stored q2 answers and still display in the Contributions Terminal and admin guestbook (label fallbacks added). (2) The entity question (q3) lost its parenthetical hint - the mint response now carries the visitor classifier verdict (author_label + author_desc), shown above the field as an OUR READ line for the visitor to confirm or correct; the plain-text instructions endpoint tells AI visitors the same. (3) The Leave Your Mark button no longer appears over the loading screen - born hidden, fades in as the boot veil fades out. (4) Field sizing: q3 capped at 60 chars server-side and rendered as a one-line input (short-cap questions auto-render as inputs), q4 textarea one row taller, name cap raised 10 to 15 chars everywhere (DB column already VARCHAR 120; mobile felt like it cut off sooner because maxlength counts emoji as 2+ units - the raise adds headroom).
6-K source steps 4-5: quarterly serving, evidence-based hold lift, report-time completion
Step 4: HexEdgarProvider quarterly serve falls back to validated 6k_pr shadow rows for hooked+reconciled filers; the no_quarterly hold becomes a per-filer turnstile (lift = hook + reconciled + reclassify through the standard classifier, which applies every other ban predicate for free). Step 5: report-time missing-quarter completion + nightly watch. Trial three (NICE/ODD/NVO) lift first; then census + graded-intersection backfill.
6-K quarterly source built: foreign filers quarterly statements from EDGAR, reconciled to the dollar
Steps 1-3 of the sixk design (filed 2026-08-26) built and trial-proven on ODD/NVO/NICE. Identify: earnings-date proximity + in-accession title sniff + a statement-weight EX-99 size witness (the ODD unlock - its results releases carry 168-233KB exhibits vs 7-21KB noise while its TD earnings dates are half-wrong). Extract: one Claude batch per run (Haiku, batch pricing, $0.008-0.033/filing), fence-tolerant JSON parsing, receipts for every reply, arithmetic + currency/units gate, outflow sign normalization to schema convention; 13 periods extracted across the cohort, false-positive candidates self-identify as not_a_results_release. Reconcile: FY-end balance-sheet equality + direct-FY / quarter-sum flow checks vs the annual XBRL, native currency from disk companyfacts (cache rows are USD-converted and banned tickers have no cache). RESULT: all three filers PASS - 32/32 gated fields within tolerance, nearly all EXACT to the dollar; one advisory finding (NVO D&A bundles impairments the XBRL tag excludes, +49.9%, correctly non-gating). Total trial spend $0.42 incl. one diagnostic re-run. Serving untouched: steps 4-5 (quarterly fallback + evidence-based hold lift + nightly watch) await William.
Post-deploy heal backlog: bigger nightly budget and gems healed first
The v1.1.582 wipe (1,171 stale rows) exceeded the 800-spawn CLI heal budget for the first time, leaving 935 rows unhealed locally and a flagged gem (LULU) public on prod for an extra day. CLI budget raised 800 to 2500 (spawns are serial - duration knob, not load), and the recompute walk now orders gem-family tickers first (open OR recently-ended gem/diamond intervals - the ended-recently half catches flag-demoted gems, which close their interval on demotion). Validated by healing the full 935-row local backlog in one pass.
Silent-filer divisor establishment armed as the default
William ruled on data-trust ledger row #10: establishment (implemented shadow-first in #856) is now the default for gate + serve. New single armed check StocksShareBasis::establishArmed() — unset or =1 armed, DELVANTIC_SHARE_BASIS_ESTABLISH=0 reverts to shadow — used by both consumers (verify() and the coordinator stamp), and the flag rides MechanicalChain spawn env whitelist so an explicit disarm survives heal spawns (the #851 whitelist-drop bug class). Decision evidence: 1,739-ticker pool sweep found zero loose passes — only 4 warm silent-class tickers (HDB foreign/stale cover, PAA no independent family cached, SQ/TSN dual-class with no derivable cover), all correctly still refusing; the only divisors ever established (HSY, BKR) were both externally verified. Post-arm proof: share-basis lab 25 ok/10 fail identical to baseline (HSY passes, QSR still refuses), pool sweep tally identical, fix-cycle #3 pit parity identical (tool verdict corrected by hand — its resolutionVersion heuristic assumes normalizer fixes; no version bump was correct here). Also fixed a stale establishDivisor docblock bullet (said DEI_FRESH_DAYS; code deliberately uses WITNESS_MAX_AGE_DAYS). Ledger row #10 closed as fixed.
Share-basis gate learns the silent-filer class: divisors established from independent families, shadow-first
Implemented the silent-filer plan same session (design #855): latestCoverShares (us-gaap cover fallback, new method - latestDeiShares untouched for its five callers incl. the classifier), establishDivisor (fresh cover + independent-family corroboration + postdates 8-K rule + foreign refusal), shadow-by-default verify() branch armed per-run by env flag, established-row stamping in both coordinator serve paths (current row only, provenance fields, no eps fabrication), tautology guard (dei chair abstains on established rows) with the vendor security-master taking the vacated bench seat so established divisors re-verify live every run. Three traps found during implementation, each now guarded and commented: the cap-mirror (stored market_cap = price x our own edgar_shares, caught by shadow mode's first output on BKR), the postdates rule (HSY's pre-cover 8-K 5.03), and the vacant bench seat. Validation: original lab specimens byte-identical throughout, 1710-ticker sweep tally identical, two new war-story specimens (HSY established, QSR conflict), BKR = first silent-filer analyzed end-to-end with the dcf dividing by exactly the established count. Benefits every run type - the paid nightly hits this same gate.
Cold-ticker scan blocked: a filer class the share-basis witness bench predates (not a normalizer bug)
First 10-ticker cold Qwen scan: 1 completed (DFS, avoid), 8 correctly held by the share-basis gate, 1 junk ticker. Root cause (corrected same evening after a wrong first diagnosis): the silent-filer class — filers that extension-tag EPS/share concepts leave SEC companyfacts with no statement share count and no EPS (extension facts omitted by the API; HSY proven end-to-end, its dei cover tag stale too), so the witness bench never convenes and verify() holds as unverifiable-not-disproven. The divisor EXISTS on disk under us-gaap CommonStockSharesOutstanding (HSY FY2025 10-K: 202,839,656). Full system deep-dive done (assess/heal/verify/gate/attestation layers + every encoded battle scar) and a fix DESIGNED, not implemented: stocks/hex/SHARE-BASIS-SILENT-FILERS-PLAN.md — Phase A widens the cover-count derivation to the us-gaap instant tag (feeds the existing dei chair), Phase B establishes a divisor for silent DOMESTIC filers only when the fresh cover count and the tape independently agree (+ no corporate action), Phase B2 stamps it onto the current row with established provenance and a tautology guard so the cover count can never corroborate itself. QSR-class multi-class conflicts stay refused. Validation: census-before-code, the class own historical corpus as regression suite, invariance sweep, HSY one-ticker proof.
Qwen lane iteration loop: baseline closed — designations match the paid panel within measured noise
Ten-iteration manual tuning loop over a 7-ticker cohort spanning deep-avoid to solid-gem. Built the compare harness (paid vs lane scoreboard + prose side-by-side) and a noise-floor tool (same-config repeat diff). Findings: designations 7/7 within noise with both gem-boundary directions repeat-tested (the one apparent miss dissolved on repeat; a boundary match survived its repeat); stance polarity never flips — the lane's only judgment defect is under-commitment at conviction extremes; temp-0 noise floor triangulated at 12-16% of numeric values across three repeats, demoting single-run magnitudes to anecdotes; peer-set re-rolls found as an upstream noise source affecting industry comparisons in both lanes. Two lane-only modifications from run autopsies (token ceiling scaling, retry-once parity), prompts untouched. Baseline frozen as the pre-adapter reference.
Fine-Tuning Qwen public page — the training chapter of the Qwen series
Fourth page of the public Qwen set (model / hardware / interface / training): teacher-student distillation from our own three-model paid panel, how the 1,991-pair training set was curated and what was dropped, QLoRA on a consumer card, the eval-first discipline, and the measured temp-0 noise floor (11.9% of values change on a byte-identical repeat). Results section deliberately empty until the adapter is measured — the static-page rule applied to ourselves. Linked from the footer Experiments column.
Qwen fine-tune track: training machine ready and panel answers exported as training pairs
Track A of the Qwen fine-tune. WSL2 Ubuntu on the 4090 with a Python 3.11 conda env (torch 2.11+cu130, Unsloth, bitsandbytes, PEFT, TRL); Triton kernels proven on the GPU inside WSL. Qwen3.8-27B weights (55.6 GB) downloaded locally. New stocks/qwen/finetune/ package: export-pairs.php rebuilds every frozen report's panel briefing via the replay gather and pairs it with each paid seat's narrative + VERDICT/STANCE (1,991 clean pairs from 679 reports / 549 tickers; pre-STANCE, partial-STANCE and peer-mentioning rows dropped; bad-UTF-8 guard); train.py runs a QLoRA adapter with a by-ticker split and pinned holdout cohort. 5-step smoke test passed end to end (loss 1.21, eval 1.18) and sized the real run: p95 4.1k tokens, VRAM peak 34.7 GB spilling past the 24 GB card, ~15 h/epoch as configured. Caveat recorded: seats keep only a briefing sha and 15/~2,000 rebuilt papers match it. Adapter is judged only through Lane Compare on held-out tickers.
Qwen Runs: the paid report pipeline with the local Qwen model as the backend, in its own tables
William: "it should mimic the e2e exactly, we are just replacing paid calls with local Qwen as the back end, but keep their data separate." Two process-env switches, set only by the lane runner (never .env, never prod): DELVANTIC_RUN_LANE=qwen rewrites every stocks_script_runs / stocks_reports statement to mirror tables (StocksRunLane, ONE site in Database::runStatement — chosen over a 45-file hand swap because a missed file is exactly the bleed the mirrors exist to prevent); DELVANTIC_LLM_ENGINE=qwen routes ClaudeService::chat/chatWithCaching/chatBatch to the local model in the same response shape (StocksQwenEngine). buildPhases drops the GPT/Grok/qwen38 seats + round 2 under the lane. Mirror tables are CREATE TABLE LIKE copies (migration 366) so every paid step writes rows it already knows, and the detail page renders a Qwen run card-for-card under ?lane=qwen (admin-only, banner). Runner scan.php spawns the real unit (run-one-full) per ticker, TD-paced, ledger in stocks_qwen_runs. Board at /stocks/admin/qwen/qwen-runs/ (new Qwen nav group). AnthropicPricing returns $0 for engine=qwen-local. Push-only sync for the ledger + mirror reports (per-step mirror not synced yet: composite key). Earlier same-day 3-read 'light scan' replaced — William's intent was the full mimic. Isolation invariant + rationale: stocks/qwen/pipeline/README.md.
Qwen v3 re-audited: reasoning on lifts narrative grades to A3/B76/C21/D0 (from A1/B58/C33/D8)
Ten readers re-read the 100 v3 (reasoning genuinely on) narratives on the same rubric -> stocks/qwen/logs/audit-2026-08-28-level1-v3-reasoning.md, attached to version 3 only. Grades A3/B76/C21/D0 vs v1 A1/B58/C33/D8; level 2 agree 60% / lean -0.39 / r 0.52. What reasoning fixed: no scratchpad in print, no refused verdicts, no wrong-sign figures, explicit arithmetic (P/E back-solves, no-growth DCFs, EV bridges), the invented facts of v1/v2 gone (PPC leverage, CRUS peak-vs-trough, FOXA $175M quarter, RELX liquidity crisis), insider codes read correctly, fixed opener gone. What survived: broken fields taken literally (D/E 0 as a fortress; LVS ROIC; VRRM corrupted revenue), bank/custodian cash-flow lens (STT, MS, BPOP), EV/net-cash bridge still missing where it matters (MNDY, TTD, ZM, LULU), and a NEW mode — wrong outside-knowledge specifics (NVS Kyntheum/Rova-T, NVO triple-agonist, MSFT 9.3% yield, LDOS SBC scandal). Those are exactly v4's worksheet targets.
Qwen v3: reasoning on (for real) doubles panel agreement — 30% to 60%, r 0.32 to 0.52, lean −1.48 to −0.39
Real reasoning-on run (think=true verified by thinking_chars, avg ~7,000 chars of working per ticker, 49 s/read, 100/100, 0 fails). vs v1 (reasoning off, same prompt, same paper, temp 0): agreement with the panel majority 30% -> 60%, mean gap −1.48 -> −0.39, spread ±1.78 -> ±0.98, r 0.32 -> 0.52, most-bearish-seat count 57 -> 17, direction changed on 64 of 99 tickers with the biggest moves all toward the panel (G, ODD, BKE, BLDR, CI, CMCSA, DVA, GM, KNSL, SCHW, STT, TASK: overvalued 2–3 -> undervalued 6–7, landing within a point of the panel). Reasoning WAS the lever after all — the earlier negative result (v2) was the bug. Also: versions now freeze the Qwen narratives + thinking_chars in rows_json (#846) after v3's narratives were nearly lost to v4's overwrite; v4 (worksheet prompt on top of v3) running.
Risk:reward v2 (distributional) is now the default; v1 kept beside it; stocks/pipeline/<step>/ package convention
(see stocks/pipeline/riskreward/README.md position log for the full state + next steps) BKE showed R:R 107 (CMCSA 402): v1 divides bull upside by a single repeat-worst-quarter downside, ~0 for any name whose recent quarters were all good. New StocksRiskRewardV2 in the new stocks/pipeline/riskreward/ package (first of the stocks/pipeline/<step>/ convention): decay-weighted mu/sigma of up to 16 matched YoY quarter pairs, stress = mu - k*sigma, upside = mu + k*sigma, sigma floored by long-run/sector/3%, Kish n_eff correction, +/-50% growth clamp, same margin bend and DCF core; payoff classes ok / none / no_downside. PROMOTED TO DEFAULT same day by William (no back-test - an opinion metric): CompositeRecompute feeds v2 to the score and Diamond gate, v1 kept beside it; board admin column shows v2 (v1). Every null R:R carries risk_reward_reason and the detail card always states why. 447 composites re-blended: 60 v2 / 124 v1-until-touched / 263 explained-null. Backfill tool exists; finishing it needs the vendor hatch (24h profile TTL under the freeze) and a run-missing mode for 195 pre-chain tickers - William deferred. MFG held under no_quarterly; IBN cannot be (no cadence row).
Nightly list: Watch-refresh lane (top 40 by score, 21-day cadence) and width 8 -> 10
William: "I feel like I could be missing deals in the top Watches" — a Watch flips on a print or a price move that a stale report cannot see. New lane C in StocksNightlyList between stale gems and the cap walk: the top 40 Watch names by composite score (ranked first, then aged) re-run when their latest report is >=21 days old, oldest first; picks and vetoes rowed to the Report Tracker as LIST/VETO . watch . ... with the watch rank and score. ~40/21 = 1.9 slots a night; migration 363 raises nightly_width 8 -> 10 so the cap walk keeps ~6 seats (+~$20/month). No replay mode for lane C (composite rows are upserted in place). Preview for 2026-08-29: 9 stale watches (one-time backlog) then 6 cap-walk names.
Qwen reasoning flag never reached Ollama — duplicate array key; v2 invalidated, real reasoning run as v3
The step's send() options carried 'think' => $think and, later in the same array, 'think' => false; PHP keeps the last key, so every "reasoning on" reading actually ran with reasoning off while the row recorded think=true (the flag I intended, not what was sent). Found while adding the worksheet prompt. Fixes: duplicate key removed; OllamaService now returns thinking_chars (message.thinking length) and the row records it — proof the flag was applied; repeat_penalty passthrough; mechanical retry once on a reply with no STANCE line. Verified: ZM with think=true -> 7,434 thinking chars, 52 s (vs 17 s). Second finding: the old v2's 41 direction changes came from num_predict 3000->12000 alone — temperature-0 output is only reproducible for identical settings (GPU batch nondeterminism on long prompts). v2 relabelled INVALID and kept as the record; the real reasoning-on run is frozen as v3 (same 100, p1 prompt, temp 0, think verified).
Qwen v2 re-audited on its own narratives; grades now belong to the version they describe (no bleed between experiments)
William: "we don't want to bleed data into each other." The v2 (reasoning-on) tab had been showing v1's grades matched by ticker. Fixed the rule: the Live tab is ungraded; a frozen version carries grades only from a level-1 audit run on its own narratives (experiment.php --grade=N --file=…; StocksQwenAudit::gradeVersion); each tab shows which audit graded it. Ten readers re-read the 100 v2 narratives on the same rubric -> stocks/qwen/logs/audit-2026-08-28-level1-temp0-think.md: A1/B55/C35/D9 vs v1 A1/B58/C33/D8 — a wash. Reasoning fixed mechanical faults (BRBR loop, PEG/DEO now commit, MSFT/COR misreads gone) but not the habits: scratchpad leaks (G, BRBR, HLNE, TTE), invented facts (PPC, CHKP, FOXA, CRUS, new: RELX, SNY, BAM), bank/insurer cash-flow errors (TD, MS, DB, SLF), a new tendency to trust the synthesis number (CM, PEP), and the fixed opener in ~100/100. Confirms the v2 conclusion: judgment frame, not compute.
Qwen AI Abilities: public page on how you actually interact with a local model
Third page of the Qwen set (/pages/qwen-ai-abilities.php): what a request and reply look like (messages in, one message out), forcing JSON with a schema (structured output — with a captured example where the schema held but the judgment was still the model's), tool/function calling and the agent loop + MCP, thinking mode with captured working, vision + coding (benchmarks, editor-integration advice, honest limits), the settings table (temperature/num_ctx/num_predict/think/repeat_penalty/seed/system), the 7-rung upgrade ladder (prompt → structured output → few-shot → RAG → Modelfile → LoRA adapter → new weights), the industry-standard layer table (OpenAI-compatible wire format, GGUF, Hugging Face, JSON Schema, function calling/MCP, Open WebUI, Continue/Cline/Aider), and what it cannot do (no memory, no embeddings — verified 501, confident wrong numbers). Every reply captured from a real local run at temp 0; no hosts/ports/paths/hardware shown. Footer link + manifest.
Graded board: member-safe tier tooltip + two admin-only drawer columns (risk:reward, Growth Outlook)
(1) The Watch/Gem badge tooltip printed the raw blend equation to everyone; non-admins now get a plain-English reading, admins keep the equation prefixed with an ADMIN VIEW line carrying the live ext-gem-composite methodology hash. (2) Admin-only columns on every drawer (StocksAuth::isAdmin(), same PHP gate): R:R = the measured risk:reward from the composite (#671/#673; dash on rows that predate it - 6/8 gems, 77/182 watch carry it today), and Growth = the Growth Outlook lens quantified (net score -100..+100 with tone, the growth call under it, tooltip with confidence + the per-rung read vs expectations). Both sortable via the existing data-key mechanism; ext-lens-growth added to the board query. Members see neither column. Files: content/home/section-lens-scoreboard.php, content/home/lens-scoreboard/board-table.php, public/assets/css/stocks/lens-scoreboard.css.
Qwen experiment v2: reasoning mode as an isolated lever
Found that the Qwen seat had run with think=false since day one (OllamaService default) while the three paid seats all reason. Added DELVANTIC_QWEN_THINK=1 (num_predict 12000), recorded think on every row and in the version config. v1 relabelled reasoning OFF. RESULT v2 (reasoning on, one lever, same 100 tickers, 28 min, 0 fails, ~17 s/read — thinking is short on these briefings): agree 30% -> 32%, lean -1.48 -> -1.48, sd 1.78 -> 1.89, r 0.32 -> 0.28, no-verdict 1 -> 0 (BRBR loop fixed). Direction changed on 41 of 99 tickers with mean |dbull| 1.01, yet alignment did not improve — CHKP overval 3 -> underval 8, CM overval 2 -> underval 7, ET underval 7 -> overval 2 on identical data. Reasoning at default effort reshuffles the verdicts; it does not anchor them. Conclusion: the seat lacks a valuation FRAME, not compute — the next lever is the prompt worksheet (v3), not more thinking.
Qwen experiments are now frozen versions with tabs — change one thing, re-run, compare
William: "timestamp + tab system so we can keep this data, run an experiment, re-run the template, compare versions." New stocks_qwen_experiments table (migration 363) holds a frozen version = config (model / prompt_version / temperature) + stats + the full per-ticker dataset. stocks/qwen/tools/experiment.php --snapshot/--list/--show/--compare. The Qwen step now records prompt_version on every row and selects alternative system prompts from stocks/qwen/prompts/<version>.md via DELVANTIC_QWEN_PROMPT (p1-verbatim = the byte-identical grok prompt, unchanged). /stocks/admin/qwen-audit/ renders Live + one tab per version with the identical template, per-version config/notes, stat deltas vs the previous version, and the biggest movers; panes draw lazily. v1 frozen = baseline (temp 0, corrected paper, p1-verbatim): agree 28/92, lean -1.48, r 0.32.
Qwen vs the Panel: in-house admin page showing where the local Qwen critique lands next to the paid seats
New /stocks/admin/qwen-audit/ (William: "make the page in-house so I can visit it" — no artifacts). StocksQwenAudit reads the four seats live from stocks_script_runs and, when the level-1 audit file exists on the machine, the auditor grade + note per ticker. Four headline tiles (agreement, lean, spread, r) computed server-side to match the level-2 report; six client-side inline-SVG views: per-ticker panel-range strip with Qwen dot + filters + hover note, overlaid bell curves (Gaussian KDE of each seat's 0–10 scores with mean ticks — Claude 5.1±1.1, GPT 5.6±1.5, Grok 5.9±1.1, Qwen 4.0±1.8), gap histogram, Qwen-vs-panel scatter, direction confusion grid, grade bars. Page-level CSS override re-grounds the admin content column in the warm palette. Nav entry under Hugging Face Repos.
Qwen two-level audit on 100 replay readings (corrected paper, temperature 0)
Level 1: 100 tickers x 4 seats read in full by 10 parallel readers on one rubric -> stocks/qwen/logs/audit-2026-08-28-level1-temp0.md (grades A1/B59/C32/D8 vs A0/B19/C30/D29 on the pre-fix paper; the 2021-as-latest defect is gone in 100/100). Level 2: meta-analysis -> stocks/qwen/logs/meta-2026-08-28-dependability.md updated with the temp-0 set: direction agrees with the trio majority 28%, Qwen -1.5 bull points below the trio on 71% of names (a stable lean), r(Qwen, trio) +0.32, top-quartile overlap 8/24 (chance ~6), repeatability at temp 0 = 10/10 identical (was 4/10 at 0.7), 0.7->0 moved 57% of directions on the same paper. Residual defects are judgment not parsing: over-weighting signals the paid seats discount (bank OCF, FCF CAGR for lenders/insurers, tiny insider sales), promoting the trio bear paragraph to the verdict, occasional invented facts (PPC leverage, BAM baseline), scratchpad leaks (G, CHKP, MSFT), one temp-0 repetition loop (BRBR, no verdict). Verdict unchanged: useful as a bearish second opinion; not scan-grade.
Qwen seat made deterministic (temperature 0) after a same-paper repeatability test flipped 6 of 10 verdicts
With the replay paper now byte-identical to the panel's (#836), the 10-ticker repeatability pass showed the Qwen seat changing direction on 6/10 tickers and moving 1.7 bull points on average when reading the identical briefing twice at temperature 0.7 (AXP: overvalued 3 -> undervalued 6). That noise is larger than its disagreement with the paid seats, so nothing about its judgment was measurable. Temperature is now 0 for the Qwen seat only (paid seats unchanged; DELVANTIC_QWEN_TEMP overrides) and recorded on the row. All 88 replay rows re-run at 0, repeatability re-measured. Data eras (pre-fix deprecated / corrected-unproven / hash-proven) documented in stocks/qwen/README.md.
176 updates shipped
Combat
| Health Points | ... |
| Attack Damage | ... |
| Attack Range | ... |
| Swing Duration | ... |
| Damage Timing | ... |
| Cooldown | ... |
Movement
| Patrol Speed | ... |
| Chase Speed | ... |
Detection
| Aggro Range | ... |
| De-aggro Range | ... |
| Leash Range | ... |
Death
| Death Effect | ... |
| Respawn Delay | ... |
| Debris Duration | ... |
AI Behaviour
The skeleton warrior cycles through five states. Transitions happen based on proximity to the player and internal timers.