Skip to content
/notes

How I Use Agents to Maintain JMTE

·10 min read
A woman with long brown hair seen from behind, reviewing a Europe map interface with city signals and candidate queues.

A flower workshop once showed up in Just Move to Europe's founder calendar with a JMTE pick.

The importer had found it on an approved source calendar. Its title contained two words the classifier liked: "workshop" and "launch." Those words often appear in builder events. Here they described bouquets and summer scents. Nothing crashed. The row looked plausible, which made the mistake easy to miss.

JMTE's importer and event classifier are deterministic TypeScript, so the fix was concrete. I added the failure to the labeled eval set and tightened the rules that separate builder activity from lifestyle language. The same case now has to pass every time the classifier changes.

That incident clarified what I had actually built.

I built JMTE to answer a practical question: where in Europe can you build both a life and a company? Keeping it useful requires research, source checks, event discovery, feedback triage, cost evidence, and a lot of editorial judgment. Agents help me maintain it. Deterministic code handles repeatable decisions. I still own the policy, the exceptions, and the public promise.

Three kinds of work

I now separate the work like this:

agent-assisted maintenance -> research, audits, proposed changes
deterministic runtime      -> parsing, rules, dedupe, snapshots
human governance           -> policy, exceptions, public claims

Agents work around the repository. They inspect sources, review code and content, propose edits, run audits, extend evals, and implement changes after I authorize them.

The runtime is ordinary software. Typed Node scripts fetch event pages, normalize records, classify feedback, check links, generate cost-trust snapshots, and move approved event candidates into the public table. The product does not call an LLM to decide whether a flower workshop belongs in Berlin's founder calendar.

Human governance sets the bar. I decide which sources are allowed, what counts as a useful founder event, which claims need primary evidence, which mistakes are expensive, and when automation has earned permission to publish within those rules.

If I called all three layers "the agent," the system would sound more autonomous and less accountable than it is.

The event pipeline is the clearest example

JMTE starts with an approved source calendar. A parser extracts the title, date, location, source text, and event URL. City inference stays conservative. Broad source pages cannot stand in for an event detail page, and suspicious location matches are held or dropped instead of being assigned to the nearest JMTE hub.

Each parsed event is classified before it is written. Deterministic review rules return one of three outcomes:

approved -> strong builder fit under the current policy
pending  -> ambiguous, incomplete, or high-context
rejected -> clear noise, wrong geography, or expired

Approved and pending rows land in event_import_candidates, a private Supabase table. In the normal automated run, clear rejects are dropped and recorded in the run artifacts instead.

The scheduled workflow runs the event-classifier eval before every weekly import. Clear builder events can be approved and promoted from that same run. Ambiguous rows remain in the private queue for review.

Human governance does not mean I click approve on every event. The judgment lives in the source allowlist, the classifier rules, the labeled edge cases, the promotion scope, and the decision to let high-confidence rows publish automatically. I step back in when the system lacks enough evidence or when the cost of being wrong is higher.

There is also a manual path. I can run the importer without promotion, inspect a dry-run artifact, and promote an exact import run, source, platform, or set of event URLs. Live promotion refuses an unscoped write unless I deliberately request all approved candidates.

On the normal success path, promotion writes the public event ID back to the candidate, alongside its review note, source, and import run. One improvement still on my list is persisting the classifier policy version with each run, so the audit trail shows which exact rules made the decision.

Taste becomes an eval set

Event relevance sounds subjective until a mistake forces the standard into words.

For JMTE, the question is whether I would tell a serious founder, operator, engineer, or AI builder to spend an evening, a weekend, or a train ride on this event. A source calendar is discovery, not endorsement.

That bar creates awkward edge cases. A founder-house opening can be useful even when the format is social. A monthly AI breakfast can still be a real builder room. Startup-hub karaoke is usually noise. Generic networking stays pending until there is more evidence. Crypto events need concrete protocol or developer evidence. "AI," "startup," and "workshop" are signals, never verdicts.

The classifier encodes those decisions as weighted signals, hard conflicts, trusted-source rules, and specific exceptions. The test corpus records the examples I want future changes to preserve. The flower workshop sits next to builder hackdays, technical meetups, founder socials, book clubs, student workshops, and non-European events discovered through Europe-wide calendars.

Every false positive can become a permanent regression case. That is how taste becomes maintainable without pretending it has become objective.

Different content needs different gates

Events are the most automated surface. The rest of JMTE uses the same trust principle through different mechanisms.

SurfaceMaintenance workWorking statePublic path
Feedbackredact, classify, route, dedupeprivate agent_review_itemshuman turns the signal into a task or approved edit
Eventsfetch, parse, classify, derive tagsprivate event_import_candidatesscoped promotion, automatic for policy-approved rows or manual for reviewed rows
Resourcesaudit links, inspect fit, check sourcesversioned resource data plus private review itemsexplicit source-checked Git change with row-level provenance
Costsrecord source checks and accepted feedbackprivate evidence tablesgenerated trust snapshot; calculator values change through a separate reviewed edit
Capitalsource-check and curate rowscurated Supabase recordsrows selected for public inclusion

This distinction matters. A feedback item marked approved does not rewrite a city page. A link-health failure does not delete a resource or refresh its provenance date. A cost source check does not silently change a calculator assumption. Each status means something inside its own workflow.

I resisted forcing every surface into one universal candidate schema. The event pipeline and feedback queue solve different problems. The private ops route brings them into one review surface through an adapter, while their underlying tables keep the fields and transitions they actually need.

The data models differ, but the rule stays the same. Generated or automated work must remain inspectable, and public state needs a deliberate path.

The private queue is routing infrastructure

The feedback queue shows why that path cannot be generic. A message can contain an email address, personal context, a correction, a sales pitch, or a claim that sounds confident and happens to be wrong.

The triage script redacts email addresses and phone numbers from the review summary, detects cities and URLs, assigns a category and suggested action, and stores the remaining metadata in a private review item. The classification is deterministic. A trust-related phrase such as visa, tax, salary, rent, or grant routes the item toward source verification. A submitted URL becomes a resource candidate. Unclear messages wait for manual review.

The raw feedback and operational metadata stay behind row-level security and the private /ops route. privacy_safe_summary makes the queue safer to scan; it is still a private operational field, not public copy.

An approved review item still does not publish anything. The queue helps me decide what should happen next: source-check a claim, update a resource, evaluate a city, scope a product change, draft a reply, or mark the signal done. Feedback and resource items still have no promotion helper. I want that missing piece to remain missing until its authority is clear.

Freshness belongs to the claim

A deploy date says when the software changed. It says nothing about whether someone rechecked a visa page, a transit fare, or a funding program.

JMTE keeps freshness at the smallest practical scope. A resource row has its own lastCheckedAt. Cost evidence has checked_at. Capital rows have source_checked_at. Untouched rows keep their previous dates, even when the rest of the site ships.

Freshness is one dimension of evidence. The cost system also keeps evidence types separate. An official source, a market listing, a market report, community feedback, a manual review, and a calculator assumption do not carry the same weight. They live as distinct source_kind values instead of collapsing into one confidence score.

The public cost-trust snapshot currently exposes counts of usable feedback and source checks. It does not automatically rewrite the calculator. If an exact value changes, that still needs scoped reasoning and verification.

A site-wide "updated" stamp would be easier. Row-level dates are more honest.

The context layer behind the agents

These boundaries only hold if the agents working around the repository know they exist. The product runtime does not contain an autonomous swarm. The agents are collaborators around the repo, and they need context to avoid undoing decisions faster than a human contributor could.

Mini-Me supplies that context and behavior system. Shared skills define how an architecture review, source audit, UX pass, or substantial implementation should run. The private vault holds my source material, voice, and project context. The JMTE repository owns the exact code, data, tests, handoff, and decisions.

Project-specific skills add the domain layer. They define how to audit city content, review imported events, check resource accuracy, update cost assumptions, or protect source dates. The intent gate comes first: review stays read-only, research stays preflight, and implementation begins after explicit authorization.

Agents still get things wrong. The system makes their expected behavior inspectable and gives the next one somewhere better to start than the last chat transcript.

The maintenance tax is part of the system

Context reduces drift, but architecture still moves.

Documentation can describe a manual event flow after the scheduled workflow has learned to auto-promote. A project handoff can fall behind the latest commit. A deterministic summary can accidentally feed language back into a later classifier unless source text and derived text remain separate. A public projection can grow broader than its original purpose. Tests can preserve yesterday's taste while the product bar moves.

That drift is the maintenance tax of automation. I want each failure to have an owner, a place to stop, and a route into a test, a narrower permission, a better projection, or a corrected decision record.

JMTE is agent-assisted and increasingly automated. Some paths end in human review. Others can publish automatically because the judgment has already been encoded, scoped, and tested.

The work is deciding which path each public claim deserves.