Signal Desk finds relevant conversations, ranks them as leads, writes reviewable inbox cards, and helps draft outreach replies. I built it with Aura’s phased workflow, visible diffs, validation, and cheap models for about ten cents.
While building Aura, I try to greenfield real projects with it as often as possible.
Not toy prompts. Not “make a todo app.” I mean projects that are actually useful, a little messy, and close to the kind of work I do in real life.
Most of the time, I run the same project twice: once before a major Aura change, and once after. It becomes a practical A/B test:
- Did Aura get faster?
- Did the output get cleaner?
- Did the project shape improve?
- Did I have to fight it less?
That is the test I care about, because real codebases do not behave like polished demos. They evolve in phases. Requirements shift. Some parts are obvious, some are fuzzy, and the tool has to stay useful through all of it.
For this release, the dogfood project was Signal Desk, a local lead discovery and outreach tool.
What Signal Desk Does
Signal Desk scans sources like RSS and Hacker News, turns relevant posts into candidates, scores them as potential leads, writes reviewable inbox cards, and helps draft replies.
The product loop is:
sources → candidates → dedupe → scoring → findings → inbox cards → show / ignore / reply
That is the kind of workflow I like testing Aura against. It is small enough to build quickly, but real enough to expose whether the generated code has an actual product shape.
The Setup
For this run, I used cheap models for both planning and implementation.
Aura running Planner and Worker on DeepSeek V4 Flash. The full dogfood build cost about ten cents in model usage._
That cost matters, but the point is not just “cheap tokens.”
The point is that cheap models are getting good enough that the harness around them matters more and more. If Aura can keep the work focused, visible, and validated, low-cost models can go much further than people expect.
The model is the fuel.
Aura is the harness that turns it into controlled work.
My Aura Workflow for Larger Projects
For larger projects, I usually do not ask Aura to build the whole thing in one giant prompt.
My workflow is:
- Write a real spec.
- Ask Aura to split it into phases.
- Dispatch one phase at a time.
- Let Aura execute the slice.
- Inspect the output, validation, files, and commits.
- Move to the next phase.
This keeps the model focused, and it keeps me in control.
Instead of reviewing a huge mystery blob at the end, I can inspect each slice as it lands.
The Phase Plan
Signal Desk was split into phases:
- Phase 1: Project skeleton and init command
- Phase 2: Data models and storage
- Phase 3: Source adapters
- Phase 4: Scoring and ranking
- Phase 5: Reply drafting
- Phase 6: Inbox and remaining CLI commands
- Phase 7: Tests and polish
Aura starts from a real spec, then turns it into a focused phase with concrete files and validation targets._
That phase structure matters. Aura is not just being asked to “make an app.” Each dispatch has a focused job.
The Flow Is the Feature
My favorite part of using Aura is the flow.
When Aura is working well, it feels smooth and quick. Planner shapes the slice, Worker moves through the files, validation runs, the project tree changes, and the output starts becoming real.
That is the feeling I am chasing: AI coding that does not feel like wrestling. It feels like watching the work click into place.
A good run looks like this:
spec → phase → files → validation → next phase
Not chaos. Not blind trust. Not “hope the agent did the right thing.”
Just a controlled build loop.

Caption: Phase 1 completed with acceptance criteria checked, validation run, and a clean handoff to the next phase.
What Aura Built
By the end, Signal Desk had:
- an installable Python package
signal-desk init- local config and workspace setup
- SQLite storage
- RSS and Hacker News sources
- candidate deduplication
- deterministic scoring
- markdown inbox cards
- reply drafting
-
scan,watch,inbox,show,ignore, andreplycommands - 109 tests passing
Caption: By the end, Signal Desk had a full project structure and 109 tests passing.
The final user loop looked like this:
scan sources → rank leads → review inbox → inspect details → ignore or draft a reply
That is the difference between generated files and a usable product slice.
Example: The Main Product Loop
This is the kind of output I am looking for from Aura.
The scan command is not just a placeholder. It follows the actual product flow.
def _run_scan(config_dict: dict) -> dict:
started_at = datetime.utcnow()
conn = db.init_db(paths.get_db_path())
logging_setup.setup_logging(paths.get_logs_dir())
sources_cfg = config_dict.get("sources", {})
scoring_cfg = config_dict.get("scoring", {})
dedupe_days = scoring_cfg.get("dedupe_days", 14)
source_instances = []
if sources_cfg.get("rss", {}).get("enabled", True):
source_instances.append(RSSSource(sources_cfg["rss"], config_dict))
if sources_cfg.get("hackernews", {}).get("enabled", True):
source_instances.append(HNSource(sources_cfg["hackernews"], config_dict))
all_candidates = []
for source in source_instances:
candidates = source.fetch()
all_candidates.extend(candidates)
new_candidates = []
for candidate in all_candidates:
existing = find_candidate_by_url_or_normalized_title(
conn,
candidate.url,
candidate.title,
dedupe_days,
)
if existing is None:
new_candidates.append(candidate)
findings = rank_candidates(conn, new_candidates, config_dict)
for finding in findings:
if finding.priority in ("high", "medium"):
write_inbox_card(finding, paths.get_inbox_dir())
insert_scan_run(conn, scan_run)
What I like here is the shape:
config → sources → candidates → dedupe → scoring → inbox → scan record
That is a real product loop. The code is not trying to be impressive. It is trying to be useful.
Example: Domain-Shaped State
Signal Desk has a simple domain:
- sources produce candidates
- candidates become findings
- findings go into the inbox
- scan runs record what happened
@dataclass
class Candidate:
id: str
source: str
source_item_id: str | None
title: str
body: str
url: str
author: str | None
created_at: datetime | None
fetched_at: datetime
raw: dict
@dataclass
class Finding:
id: int | None = None
candidate_id: str = ""
source: str = ""
title: str = ""
url: str = ""
score: int = 0
priority: str = "low"
reason: str = ""
best_angle: str = ""
avoid_note: str = ""
draft_reply: str = ""
status: str = "new"
@dataclass
class ScanRun:
id: int | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
candidates_found: int = 0
new_candidates: int = 0
findings_created: int = 0
errors: str | None = None
This is boring in the right way.
The names match the product. The state is explicit. The rest of the project has something clean to build around.
That is the kind of code I want from AI: not generic DataManager soup, but simple concepts that belong to the thing being built.
Example: Scoring That Explains Itself
For a lead discovery tool, scoring needs to be inspectable. I do not just want a number. I want to know why something landed in the inbox.
def score_candidate(candidate: Candidate, config: dict) -> dict:
aura_cfg = config.get("aura", {})
include_kw = aura_cfg.get("include_keywords") or []
pain_kw = aura_cfg.get("pain_keywords") or []
avoid_kw = aura_cfg.get("avoid_keywords") or []
title = candidate.title or ""
body = candidate.body or ""
combined = title + " " + body
score = 0
reason_parts = []
avoid_note_parts = []
for keyword in _check_exact_matches(title, include_kw):
score += 35
reason_parts.append(f"Matched '{keyword}' in title (+35)")
for keyword in _check_exact_matches(body, include_kw):
score += 20
reason_parts.append(f"Matched '{keyword}' in body (+20)")
if _has_alternative_intent(combined):
score += 10
reason_parts.append("Alternative intent detected (+10)")
for keyword in _check_exact_matches(combined, avoid_kw):
score -= 40
avoid_note_parts.append(f"Avoid keyword '{keyword}' matched (-40)")
score = max(0, min(100, score))
return {
"score": score,
"reason": "; ".join(reason_parts) or "No strong signals detected",
"best_angle": best_angle,
"avoid_note": "; ".join(avoid_note_parts),
}
This is exactly what I want for this kind of tool.
The scoring is deterministic. The reasons are visible. The user can inspect why something matched before deciding whether to respond.
Example: Reviewable Inbox Cards
Signal Desk does not just dump rows into a database. It writes markdown cards that can be reviewed directly.
def format_finding_card(finding: Finding, include_draft: bool = True) -> str:
priority_emoji = {
"high": "🟢",
"medium": "🟡",
"low": "⚪",
}.get(finding.priority, "⚪")
lines = [
f"# {priority_emoji} {finding.priority.upper()} · score {finding.score}",
"",
f"**Source:** {finding.source}",
f"**Title:** {finding.title}",
f"**URL:** {finding.url}",
f"**Status:** {finding.status}",
"",
"## Why this matters",
finding.reason or "No reason recorded.",
"",
"## Best angle",
finding.best_angle or "No angle recorded.",
"",
"## Avoid",
finding.avoid_note or "None",
]
if include_draft and finding.draft_reply:
lines += ["", "## Draft reply", finding.draft_reply]
return "\n".join(lines)
That is product thinking.
The user’s job is not “own a database.” The user’s job is to review useful conversations and decide whether to respond.
The output supports that job.

Caption: Signal Desk generated ranked lead cards from scanned conversations.
Example: Reply Drafting
Signal Desk also helps draft replies from a finding.
It supports different styles and a no-Aura mode for cases where I want to respond without mentioning the project directly.
def generate_reply(
finding: Finding,
style: str = "helpful",
no_aura: bool = False,
) -> str:
ctx = _build_context(finding)
if no_aura:
reply = no_aura_template(ctx)
elif style == "technical":
reply = technical_template(ctx)
elif style == "short":
reply = short_template(ctx)
else:
reply = helpful_template(ctx)
if no_aura and "aura" in reply.lower():
reply = no_aura_template(ctx)
return reply
Again, this connects back to the workflow.
Signal Desk is not just finding leads. It helps move from discovery to outreach.
Why This Matters
What I liked about this run was not that Aura generated a lot of code.
It was that the code had a shape I could keep working with.
The CLI followed the user flow. The models matched the domain. The storage layer served the product. The scoring explained itself. The inbox output was reviewable. The reply drafting connected back to the findings.
That is what I want from AI coding:
- not a pile of files
- not blind trust
- not an expensive black box
- not code I have to babysit forever
I want a useful slice I still control.
That is what Aura is built around.
Where Aura Is Headed
Aura is an open-source desktop AI coding harness.
The goal is simple:
Ask for something real.
Break it into phases.
Keep control.
Review the work.
Validate the result.
Spend almost nothing.
Get code that feels like it belongs in the repo.
That is the workflow I want to build with every day.
Aura is open source on GitHub:



Top comments (0)