I am having a problem. The agents we have made sometimes make great progress, and sometimes get blocked and don't do anything. They make crons that break, and keep forgetting instructions I have given them, and forgetting to write important things to memory. I am wondering if I am structuring the agents in the right way. Rather than having one agent try to do everything in a job (planning and executing), should I break each project up into two agents, one that plans and manages the project, while the other is focused on execution? I often have the agents move between tasks, and they lose context in between tasks. What is the best way to keep agents able to execute tasks that they learn? do have them only do one job, and create another agent to do the next job? I am worried that the we have way too many agents. I also have very different job types, one is compiling a research document, and another is going thorough a database of videos to organize them to be used in a larger storytelling system. So each project has a big scope to keep track of, but a series of steps needed to execute the whole plan (in order to create a storyteling system I need the system to break the unedited videos into scenes, tag each scene with people and events, describe each scene and create an admin web interface to browse the scenes and make corrections, then another web interface for rgular users to combine the scenes into stories. I have maybe 5 projects of this level of complexity I am trying to orchestrate at the same time, and I am not making much progress. I also have automated processes that I need to run reliably to maintain the system, so do I need another agent for that? I am not sure how to organize the agents I have to work in the most effective way. This is the structure of my current agent council. I have tried to implement a self-improving ACT system, but the agents have not improved since putting the system into place. I have also implementred a trello project system.
#Agent structure and reliability
1 messages ¡ Page 1 of 1 (latest)
HEre is my structured council: đż Juniper (Main / Orchestrator)
Mission: Phillip's primary assistant â coordination, memory, infrastructure, agent management
Active projects: None (idle, orchestrating other agents)
đ° Hazel (Ops / Infra)
Mission: Keep everything running â cron health, Scotty monitoring, service uptime, config safety
Active projects:
Nightly Daily Maintenance cron (cron errors, LaunchAgents, osxphotos, Immich job report)
Hourly MiniMax usage monitoring
Immich auto-restart infrastructure (pending LaunchAgent install)
đ¸ Rosemary (Research)
Mission: Deep research + structured data pipelines
Active projects:
Watch Vault research pipeline + DB population
Immich face detection / watch image pipeline
NocoDB integration
Watch Research cron (6am daily)
đ Holly (Home Automation / Property)
Mission: Smart home, HA integration, property management
Active projects:
Greenwood Lake & NYC Property Handbook
No active Trello jobs currently
đŤ Olive (Reel Archive)
Mission: Classify and organize the reel/Super 8 archive
Active projects:
v11 Reel Project 907 Detection Reclassification (AGENT JOBS)
đž Willow (CAD / Fabrication)
Mission: 3D modeling, STEP/STL generation, fabrication specs
Active projects:
Bell Mechanism for Digital Hourglass (AGENT JOBS)
đ¨ââď¸ Judy (ACT Judge)
Mission: Test, score, and improve all agents via ACT v2 protocol (3-gate compliance)
Active projects:
Running Hazel capability test
Owns ACT 2 Protocol Dashboard (Trello)
Queued: Rosemary, Holly, Olive, Willow tests after Hazel passes
Pain Points Worth Discussing
Before you ask for structural advice, here's what I'd flag:
Output visibility â agents complete work but results often live in files nobody sees (logs, md files, json). No unified "what did agents produce today?" view.
Sandbox restrictions â just fixed this morning; agents were blocked from shared workspace files.
Trello as the only signal â card updates are the main output mechanism; easy to miss or get stale.
No agentâagent handoffs â agents work in parallel but rarely hand artifacts to each other cleanly.
ACT testing still in early stages â Judy is new, capability baseline not yet established for most agents.
What's the robustness/output problem you're trying to solve? That'll help me give you targeted structural advice.
Youâre running into the two classic failure modes of âagent councilsâ:
- Context is not a database (agents will forget unless you force a re-load of the right artifacts every run)
- Multi-project + multi-agent = coordination tax (you spend the budget on switching and re-planning instead of shipping)
A structure that usually works better is hub-and-spoke per project, with very few long-lived agents.
1) Donât make âmore agentsâ your scaling strategy
If you have 5 big projects, you donât want 25 semi-specialized agents. You want:
- 1 Project Coordinator per project (persistent)
Owns: roadmap, current milestone, backlog, definitions of done, and integration. - A small pool of Workers (often ephemeral / task-scoped)
They execute one ticket, produce an artifact, then stop.
That keeps âmemoryâ localized: the coordinator maintains continuity; workers donât need to remember anything beyond the ticket packet.
2) Split planning vs execution â but do it with artifacts, not vibes
Yes, separating âplanner/managerâ from âexecutorâ is a good idea, if the handoff is a concrete document.
Use a Task Packet format that every worker gets (and must follow). Example:
Task Packet
- Goal (1â2 sentences)
- Inputs (links/files/db tables + where they live)
- Output (exact deliverable: file path, schema, checklist, PR description, etc.)
- Done criteria (bullet list thatâs testable)
- Constraints (donât touch X, timebox Y, tools allowed)
- Write-back (where to record results + what to update)
If you do only one thing: do this. It stops âblocked and doing nothingâ because the agent has a narrow, finishable contract.
3) Create a âProject Notebookâ per project and force every run to read it
Agents âforgetting instructionsâ is usually because the instruction only lives in chat history.
Make one canonical file per project (or a small set), e.g.:
PROJECT.md(what weâre building, current milestone, glossary)BACKLOG.md(tickets in Task Packet format)DECISIONS.md(the stuff you keep re-explaining)STATUS.md(todayâs focus + blockers)
Then add a rule to the coordinator agent like:
- âAt the start of every session/run: open PROJECT.md + STATUS.md, then pick exactly one backlog ticket.â
This is how you turn âmemoryâ into something reliable: the agent rehydrates context from files, not from hoping the model recalls it.
4) Stop moving agents between tasks; move tickets between agents
Context loss happens when you say ânow switch to the other thing.â
Instead:
- Keep the coordinator always in the same project scope.
- When priorities change, the coordinator updates
STATUS.mdand creates/chooses the next ticket. - Workers only ever see the ticket + required inputs.
Switching projects should look like swapping which coordinator youâre talking to, not dragging one agent across five worlds.
5) Crons need to be âops-gradeâ: tiny, idempotent, and self-reporting
Donât let feature-building agents write production crons unsupervised.
Have either:
- One dedicated Ops/Maintenance agent (only responsible for reliability), or
- A rule: âCrons can only be created/edited via the Ops runbook + review checklist.â
Cron checklist that prevents most breakage:
- Idempotent (safe to run twice)
- Timeboxed (has a max runtime / clear exit conditions)
- Self-reporting (always emits success/failure + key metrics)
- Graceful failure (doesnât silently succeed with partial output)
- Minimal dependencies (few moving parts)
Also: if a cron is important, it should write to a log channel/output every run. Silent crons are how you get âit broke last week and I didnât notice.â
6) âSelf-improving ACTâ usually stalls because thereâs no tight feedback loop
Agents donât âimproveâ just because you tell them to. They improve when you:
- Define what success looks like (metrics / acceptance tests)
- Run small iterations
- Do a retro that updates the Project Notebook / runbook, not just a chat message
Make retros extremely small:
- âWhat failed?â
- âWhat rule would have prevented it?â
- âAdd that rule to DECISIONS.md / the runbook.â
Thatâs real compounding.
7) Practical recommendation for your situation (5 big projects)
Right now Iâd do:
- 5 coordinators (one per project)
- 2â3 worker archetypes max (e.g., Research Worker, Data/Labeling Worker, Builder Worker)
- 1 Ops agent for crons / monitoring / backups / routine maintenance
And impose a hard WIP limit:
- At most 1 active milestone per project
- At most 1â2 active projects per week (the rest can have âparking lotâ backlogs)
Thatâs usually the difference between âorchestratingâ and âprogress.â
If you paste your current âagent councilâ structure (names + what each does + which projects they touch), I can suggest a concrete consolidation plan (which ones to delete/merge, and what the coordinator/worker prompts should look like).
Your council is already pretty close to a workable shape (one orchestrator + a few specialists). The part thatâs breaking isnât ânumber of agentsâ so much as:
- Too much WIP per agent/project (everything is âactiveâ, so context thrash is guaranteed)
- Not enough hard artifacts (instructions live in chat â agents âforgetâ)
- Crons are being treated like autonomous work, instead of ârun a deterministic thing and reportâ
- Judy is testing agents, but the system isnât forcing improved inputs/outputs, so nothing compounds
Below is how Iâd tune what you have without exploding agent count.
1) Reframe Juniper: Portfolio Manager + Task-Packet factory (not âorchestrate everythingâ)
Right now Juniper being âmain/orchestratorâ across all domains is the biggest context blender.
Give Juniper a very strict job:
- Own a single
PORTFOLIO.md(or Trello equivalent) with:
- 1â2 active projects total (hard WIP limit)
- the current milestone for each active project
- the next 1â3 tickets only
- Convert any request into a Task Packet before handing to any other agent
- Do not do execution except small glue work
If you only change one thing: Juniper must enforce WIP limits. Otherwise every agent will feel âblockedâ because theyâre perpetually mid-context-switch.
2) Harden Hazel: âCrons donât do work; crons run workâ
Hazel (Ops/Infra) is the right idea. The pattern Iâd enforce:
The only reliable cron pattern
Cron â run a script/workflow â summarize output â alert on failure
Not: âCron prompt: go do maintenance and think about what to do.â
So Hazel should own:
- A single Ops Runbook (what crons exist, what success looks like, how to recover)
- A single Cron Template (below)
- A rule: Only Hazel can create/modify crons (everyone else proposes changes as tickets)
Cron Template (prompt structure)
- What command/workflow to run (exact)
- What output to capture (log file / key lines)
- Success criteria (explicit)
- On failure: post the last N lines + suggested next step + create a ticket
This turns âcrons that breakâ into âcrons that fail loudly and create actionable incidentsâ.
3) Rosemary / Olive / Willow: force âbatch size + writeback contractâ
These roles make sense, but they need two constraints to stop stalls:
A) Every execution ticket must have a batch size
Examples:
- Olive: âProcess exactly 25 reelsâ (not âorganize archiveâ)
- Rosemary: âIngest 50 items into DB with schema Xâ
- Willow: âProduce STEP + 3 screenshots + BOM for mechanism v3â
Small batches prevent the âstare into the abyss and stopâ failure mode.
B) Every ticket must have a writeback location
Not âremember thisâ. Instead:
- âUpdate
STATUS.mdwith resultsâ - âAppend rows to
reels_index.csvâ - âWrite a migration file
migrations/2026-03-04_add_faces.sqlâ - âPost a checklist of what changed + linksâ
If output isnât written somewhere stable, you will feel like theyâre forgettingâbecause practically, they are.
4) Holly: either park it or make it a doc-only project
Holly having âno active Trello jobsâ is a good sign it should be paused, not kept âwarmâ.
Two good options:
- Park Holly entirely until you have explicit tickets
- Or define Holly as doc-only: handbook drafting, checklists, vendor infoâno automation, no ops
Idle agents arenât harmful, but âhalf-activeâ agents are a huge attention sink.
5) Judy (ACT Judge): change from âagent testingâ to âartifact auditingâ
This is the big one: âtesting Hazel capabilityâ wonât create compounding improvement unless the result updates the runbooks / templates / prompts that everyone is forced to use.
What Iâd do with Judy:
Replace â3-gate complianceâ with 2 recurring audits
- Ticket Quality Audit (input quality)
Sample last 5 tickets created by Juniper:- did they have goal, batch size, done criteria, writeback?
- Writeback Audit (output quality)
Sample last 5 completed tickets:- did results land in the canonical place?
- are decisions captured in DECISIONS.md (or equivalent)?
Then Judyâs only âpowerâ is:
- propose edits to the Task Packet template
- propose edits to the Ops Runbook / Cron Template
- propose âstop doing X, it never worksâ rules
Thatâs how you get actual improvement: Judy improves the system, not âthe agentâ.
6) Your âtoo many projectsâ problem is real; solve it with a weekly project lock
With 5 giant projects, you need an explicit policy like:
- Only 1 flagship build active this week (e.g. the storytelling pipeline)
- Only 1 maintenance lane (Hazel)
- Everything else is âbacklog onlyâ (tickets can be written, but not executed)
If you donât do this, your council will keep generating motion without throughput.
If you answer these 3 questions, I can suggest a very concrete re-org
- Which project is the âflagshipâ that must move forward this week?
- What does âagents get blocked and donât do anythingâ look like in practice (one example)? Silent? Re-planning loop? Tool errors?
- For crons: do you want them to (a) run scripts/workflows or (b) have the agent reason and decide actions each time?
If you reply with the flagship project + one broken-cron example (just the prompt + what happened), Iâll propose a tight âJuniper/Hazel/Judyâ template set you can copy/paste and enforce.
Those flags are basically the root cause. If you fix only âagent behaviorâ but not visibility + handoff mechanics, youâll keep feeling like nothing is happening (even when work is being done).
Hereâs the targeted structural fix Iâd apply to your council, mapped to your bullets.
1) Output visibility: build a âShip Logâ as the canonical output channel
You donât need a fancy dashboard first â you need a single, reliable place where every run deposits a summary + pointers.
Rule: every agent run must produce a Run Report in a shared location, and it must be linkable.
Run Report template (must be written every run)
- Ticket/Card:
<trello link or ID> - Produced (links/paths):
⌠- Summary (3â7 bullets):
⌠- Decisions (if any):
⌠- Next step (single):
âŚ
- Blockers (if any):
âŚ
Storage pattern (avoids âfiles nobody seesâ)
reports/<agent>/<YYYY-MM-DD>/<timestamp>-<ticket>.md- plus
reports/<agent>/LATEST.md(always overwritten)
Then you add one daily digest job (Hazel-owned) that compiles âwhat got produced todayâ by reading those reports and posting a single summary somewhere you actually look (Discord channel, email, whatever your habit is).
This alone usually fixes the âagents did stuff but I didnât noticeâ problem.
2) Trello as the only signal: Trello should be the backlog, not the output
Keep Trello, but change the contract:
Rule: a Trello card is not âdoneâ until it contains:
- a link to the Run Report, and
- a link to the primary artifact (doc/db table/video index/etc.)
So Trello becomes an index pointing to real artifacts, not the artifact itself.
If cards get stale, itâs because updating Trello is being treated as âthe workâ. Flip that: work produces artifacts; Trello points to them.
3) No agentâagent handoffs: add an explicit âArtifact Handoffâ object
Parallel work fails when nobody knows what to read next.
Add one tiny structure to every ticket that produces something another agent needs:
Handoff block (inside the Run Report)
- Artifact:
what it is + link/path - Consumer:
which agent - How to use:
exact steps / query / file(s) to open
- Acceptance check:
how consumer verifies itâs usable
And make Juniper enforce it. Juniperâs job becomes: ensure every producer creates a handoff that a consumer can actually consume.
4) Sandbox restrictions: now that shared workspace is fixed, enforce âwriteback or it didnât happenâ
This is the discipline piece:
- If an agent canât write to the shared place: it must stop and file a blocker (in the Run Report + Trello).
- If it wrote to some local/log-only place: it must copy/summary-link it into the shared report structure.
Youâre basically creating a âbusâ that all agent work travels on.
5) ACT/Judy: donât âgrade agentsâ yet â grade compliance with the system
Early-stage ACT is fine, but aim it at the mechanics you need:
Judyâs first audits should be:
- Do runs produce Run Reports?
- Do reports include artifact links + next step + blockers?
- Do Trello cards link to the report/artifact?
That will improve output reliability far more than capability scoring right now.
Quick question so I can recommend the one thing to implement first
If you had to pick one robustness outcome, which is it?
A) âEvery day I want a reliable âwhat did agents produce?â digestâ
B) âI want fewer blocked/stalled runsâ
C) âI want clean handoffs so parallel work actually composesâ
If you answer A/B/C, Iâll tell you exactly which agent should own it (Juniper vs Hazel vs Judy), and the minimum rules/templates to roll out without adding more agents.
A and B. I need both, I need to know what did agents produce, but not wake up to find out they were blocked the entire night, or killed a process that it thought was "taking too long" but that I am willing to devote time to, because on a local model, we are not paying for tokens, so slow and steady and accurate and correct is betrer then fast and inaccurate.
Then you want one unified system that does (A) daily âwhat shippedâ and (B) live âsomethingâs stuckâ alerts, without agents âhelpfullyâ killing slow long-running work.
The good news: A and B can be solved by the same mechanism: standardized run reports + heartbeat-style progress updates.
The core rule set (works especially well with slow local models)
Rule 1 â Every run has a Run Report (single source of truth)
Each agent run must write a report that is easy to find and link. Minimum fields:
- Ticket/Card ID (or âad-hocâ)
- Status:
running | blocked | done - Last update timestamp
- Produced artifacts (links/paths)
- Next step (one line)
- Blockers (if any)
If nothing else happens, this alone gives you (A): you can compile these into a daily digest.
Rule 2 â âNo silent runningâ: progress heartbeat every N minutes
For overnight jobs, require: update the Run Report at least every 20â60 minutes (pick one). Even if itâs just:
- âStill runningâ
- âProcessed 312/2000 itemsâ
- âETA unknown, but advancingâ
This solves (B) because âno updateâ becomes detectable.
Rule 3 â Never self-kill long tasks (checkpoint instead)
For your use case (âslow is fine, accuracy mattersâ):
- Agents should not terminate long-running processes just because they âseem slowâ.
- If they suspect a hang, they must:
- write a checkpoint update (âno progress observed for X mins; hereâs evidenceâ),
- ask what you want (âcontinue waiting vs restart vs killâ).
If you want a single sentence policy: âWhen in doubt, wait and report; donât kill.â
Who should own what in your council
Hazel (Ops) owns: the âNight Watchâ + daily digest
Hazelâs job becomes two routines:
-
Daily digest (A): compile all Run Reports from the last 24h into one summary:
- Shipped (artifacts + links)
- In progress
- Blocked (with reason + whatâs needed)
-
Stall detection (B): look for any ârunningâ job whose Run Report hasnât been updated within your threshold (say 60â90 minutes overnight). When detected, Hazel posts an alert like:
- âOlive job stale 2h: last update 01:10, expected heartbeat 00:30â
- include the Run Report link + suggested next check
This prevents âwake up and it was blocked all nightâ.
Juniper owns: enforcing the reporting contract + batch sizing
Juniper should refuse to hand out work that doesnât specify:
- expected runtime class (short/medium/overnight)
- heartbeat frequency requirement
- what âprogressâ means (count/items/percent/etc.)
Judy owns: auditing compliance (not capability)
Judyâs early ACT should simply score:
- Did the run produce a Run Report?
- Did it heartbeat on time?
- Did it avoid self-killing long tasks?
- Did it link artifacts?
That will create actual improvement quickly.
One practical tweak for your exact pain (âkilled a process that was taking too longâ)
Add a per-job field: Kill Policy (default: ânever kill without askingâ).
Examples:
- âKill policy: never kill; report every 30 min; only stop if disk fills or explicit error.â
- âKill policy: ok to restart if no progress for 45 min and you captured logs/checkpoint.â
This gives agents clarity and stops âhelpfulâ sabotage.
Two quick questions so this becomes concrete (and not just theory)
- Where do you want the live alerts to land so youâll actually see them (Discord channel, a dedicated âopsâ channel, somewhere else)?
- Roughly how many overnight runs happen concurrently (1â2 vs 10+)? That determines whether Hazelâs ânight watchâ is a simple stale-check or needs per-agent queues.
If you answer those, Iâll propose a very tight default heartbeat threshold + digest format that fits your council (Hazel/Juniper/Judy) without adding any new agents.
OK, before we do that, I have several structural questions. 1) I have multiple milestone projects with very different outputs. Can the same council work on those projects and switch between them? Or do I need differet SETS of agents, one for each milestone project, only one set active at a time? OR is there another bettwe way to approach this? 2) Once we have a plan, is it better to archive my current setupm am start fresh with a fresh clean openclaw installation we build from scratch? Or can I reliably shihft this agent council into the new roles and pattern without haveing to start over?
1) One council for multiple milestone projects vs separate sets?
You can use one council across very different projects, but only if you change what âswitching projectsâ means.
The failure mode youâre currently describing is: agents switch projects by relying on chat context. That doesnât scale.
A better framing:
- You donât need separate sets of agents per project
- You do need separate project context artifacts (a âproject notebookâ) per project
- And you need a WIP limit so only 1â2 projects are truly âactiveâ at a time
Concretely, the best structure for most people is a hybrid:
Shared, always-on roles
- Juniper = Portfolio manager + task packet writer (enforces âonly these projects are activeâ)
- Hazel = Ops + monitoring + digest + blocker alerts
- Judy = audits the system (reporting compliance / handoffs), not âagent capability theaterâ
Project-scoped executors (can be reused, but not concurrently)
- Rosemary/Olive/Willow/etc. can work on different project types, but each run must start by loading the relevant project notebook and must output a Run Report + artifacts.
Key rule: Donât let the same executor agent hold two projects âin its headâ at once.
Itâs fine if Olive works on Project A this week and Project B next weekâjust donât do both in parallel unless you have extremely strong artifact discipline.
When would you want separate councils per project?
- If projects need different tool access / safety boundaries (e.g., one can touch production systems, one must never)
- If you truly run projects in parallel with high urgency and want isolation
- Otherwise itâs mostly extra overhead.
So: keep one core council, plus project notebooks and strict WIP limits.
2) Start fresh with a clean OpenClaw install vs shift roles in place?
You can almost always shift the existing council into the new pattern without starting over.
Iâd only recommend a âclean rebuildâ if:
- your current setup is technically unstable in a way you canât reason about (mystery crons, unknown configs, broken permissions you canât untangle), or
- youâve accumulated so much drift that you canât tell whatâs ârealâ anymore.
Otherwise, starting fresh tends to delay progress because you lose:
- working integrations (even if messy)
- history of what failed
- existing agent workspaces / accumulated artifacts
- all the little fixes you forgot you made
What Iâd do instead (safe migration path):
- Freeze changes: declare âonly Hazel can touch cronsâ while migrating.
- Introduce the new contracts in place:
- Run Report requirement
- Heartbeat/progress update requirement
- No-self-kill policy (checkpoint + ask)
- Handoff block requirement for producerâconsumer work
- Pick one flagship project and run it âV2-styleâ for 48 hours.
- Once it works, roll the same template to the other agents/projects.
If you want an even cleaner approach without nuking anything: create a âV2 councilâ in parallel and migrate one agent at a time, retiring the old behavior as you go.
If you tell me your 2 most different milestone projects (e.g. âstorytelling video pipelineâ + âproperty handbookâ), I can outline exactly which roles should be shared vs project-scoped, and what the minimum âproject notebookâ for each should contain so switching projects doesnât melt your agentsâ brains.
Here is a list of the projects I would like to complete in the next 2 months, 1) organize a set of unstructured home movie film reels into a storytelling system that different family members can use to watch the scenes, and add their own information to to make the data more rich and accurate, but also searchable so peopel can find events they want to see (like birthday parties or vacations), then generalize that system to also be able to ingest newer home movies from a large digital first library 2) Personal watch collection database and photo archive. TO take an exisiting small database of watches (100-200) with partial information about each watch, and search the internet to fill out missing information and track the value of the collection by amount spent on each watch (absolute value), and an estimated market valuation (speculative value), archive a local copy of information about the watch (press mentions, launch announcements, receipts, and list of accessories) so that a later collector can understand why I valued each watch when I purchased it, and to scan my personal photo archive and create albums of relevant watch images that are linked to each watch, 3) Property management handbook for 4-5 different properties, collect contacts, contracts, and important operating procedures for each property so that if I am not available, another person could take over management of the property, bonus if it can create operating instructions for visitors who will be staying at the property 4) a research assistant to collect information on a prospective product idea, patent searches, competitive analysis, and help document the idea for sharing with collaborators 4) Creating a personal website for me 5) creating a website for my new company with a brief description and timeline of past work 6) development of software for an embedded hardware project on arduino 7) development of a web app/ios app 8) 3D design of enclosures and physical devices for printing on a 3D printer
Youâve got 8 projects on the list (and you accidentally labeled two as â4â). In 2 months, the main risk isnât âagent structureâ â itâs WIP overload. Even with perfect agents, 8 heterogeneous projects will mostly produce partials.
So Iâd approach this as: 1 flagship build + 1 secondary build + everything else gets âthin-slice deliverables.â Your council can absolutely support that if Juniper enforces the WIP limit and everything writes Run Reports.
Below is a practical way to slice this so you actually finish things.
First: group the projects by type (because they want different operating modes)
A) Product / system builds (high coordination, lots of integration)
- Reels â storytelling system â family annotation â search
- Web app / iOS app (this is probably the same thing as #1 if youâre building UIs)
(Also âgeneralize to digital libraryâ is another phase)
B) Data/research pipelines (repeatable jobs, measurable progress)
- Watch collection DB + valuation + archive + link photos
- Product idea research assistant (dossier/patents/competition)
C) Documentation (finite, can ship as âv1 handbookâ)
- Property management handbook (+ visitor instructions)
D) Web presence (bounded, can be shipped fast)
- Personal website
- Company website
E) Hardware (depends on real-world constraints; tends to eat schedules)
- Arduino embedded software
- 3D enclosures / printable devices
Recommended 2-month plan (that fits your council)
Week 1: install the âoperating systemâ (so you stop losing output)
- Run Reports + daily digest + blocker alerts
- Project notebooks (one folder per project; even if minimal)
This week pays for everything else.
Weeks 2â7: Flagship project (pick ONE)
Iâd strongly recommend the reels storytelling system as the flagship, because itâs the most âplatform-likeâ and will naturally produce a web app.
But you must cut scope:
Reels v1 (2-month definition of done)
- Ingest a fixed subset (e.g., first 10â20 reels / N hours)
- Auto-split into scenes (even if imperfect)
- Store scenes in a DB with stable IDs
- Simple web UI:
- browse scenes
- search by tags
- family can correct/add tags + notes
- Export/backups so you donât get trapped
Reels v1 explicitly NOT included (unless you have time):
- âGeneralize to the full digital libraryâ
- iOS app (unless you do a thin wrapper after the web UI works)
- perfect face recognition / perfect event detection
Parallel âthin-sliceâ track (1â2 days per item, max)
These are great for keeping momentum without derailing the flagship:
- Personal site: one-page static âabout + linksâ
- Company site: landing page + timeline + contact
- Property handbook v1: contacts + emergency procedures + âhow to operate the houseâ checklists
Weeks 6â8: Secondary build (pick ONE)
If the reels project is going well, your best secondary is usually:
- Watch DB pipeline (because itâs measurable and can run in batches)
I would not try to seriously start Arduino + enclosures unless you decide thatâs the flagship. Hardware work is too schedule-hostile to be a âside questâ in the same 2 months.
How your existing council maps cleanly (no need for new agents)
- Juniper: portfolio/WIP cop + writes Task Packets + maintains the âwhatâs active this weekâ
- Hazel: daily digest + blocker alerts + cron hygiene (and the ânever self-kill slow jobsâ policy)
- Olive: flagship reels system execution (batch-based: âprocess 25 scenesâ etc.)
- Rosemary: watch DB pipeline + research assistant dossier pipeline
- Holly: property handbook + personal/company websites (doc + static site work)
- Willow: only if hardware is truly in-scope; otherwise keep parked
- Judy: audits Run Report compliance + handoffs (not generic âcapability testsâ)
Two decisions you need to make (and then the structure becomes obvious)
-
Which is the single flagship for the next 2 months:
- Reels storytelling system, OR
- Watch DB, OR
- Hardware (Arduino/enclosures), OR
- Something else
-
For that flagship, what is your v1 definition of done in one sentence?
Example: âFamily can browse/search scenes from 20 reels and add/correct tags in a web UI.â
If you answer those two, Iâll suggest the exact âproject notebookâ sections + the first 5 Task Packets to queue so the agents can start producing shippable artifacts immediately.
Reels is the project I have made the most progress on. so that shoud be the flagship. I think we are close to getting aprocess working alrready. If it is feasible I would like to have an agent working constantly in the background on the watch data gathering so w ehave data rready for when we switch to the watch project, and the same for the proptery handbook.' The watch research can search the web and/or scan through my existing files to pull out information that could be useful when we start the project ( there is alot there and will take time to process. The property project needs to scan the exisiting files, also gahter potentially useful information and create an index of what we have, so that wwhen we turn to it, we don't have to start that process from scratch. These agents should be be searching for relatively simple data, but structuring it so it can be used later. I want these to work int he backgorund simply beause there are many many files and emails to go through which take calendar time. IS that realistic? The embedded code and physical enclosures I think I can work with claude code and chatgpt on an individual basis to make progress until we are ready to switch it into a flagship project. I also need a way to be able to add ideas tot he descriptions of each project, as I have new ones, so that those insights are saved, even if they are needed for the current project.
Yes â what youâre describing (background âinventory/indexingâ work for Watch + Property while Reels stays flagship) is realistic, as long as you constrain it hard so it doesnât turn into open-ended research or silent failure.
The trick is to make the background agents behave like batch ETL/indexers, not âmini flagship projects.â
A workable 3-lane structure (without creating new agent sets)
Lane 1 (Flagship): Reels
- Olive (execution) + Juniper (coordination) stay focused here.
- Reels gets the integration attention, UI work, and âshippingâ energy.
Lane 2 (Background indexing): Watch + Property
- Rosemary can run Watch indexing batches.
- Holly (or Rosemary, if you prefer) can run Property indexing batches.
- These runs must be bounded, repeatable, and write structured outputs.
Lane 3 (Ops): Hazel
- Digest + blocker alerts + âdonât self-kill long jobsâ enforcement.
This lets you make progress on Watch/Property without mentally switching the whole council.
The constraints that make background work actually useful
1) Background jobs must be âindexing onlyâ (no deep synthesis yet)
Good background outputs:
- âHereâs an inventory of files/emails/attachments with metadataâ
- âHere are extracted candidates (contacts, serial numbers, purchase amounts, dates) with source linksâ
- âHereâs a queue of items that need human review laterâ
Bad background outputs:
- âI redesigned the whole watch schemaâ
- âI started building the watch web appâ
- âI drafted the entire property handbook from scratchâ
2) Every batch needs a quota (so it finishes)
Examples:
- âProcess 200 files or 60 minutes, whichever comes firstâ
- âExtract metadata from exactly 50 emailsâ
- âEnrich exactly 10 watchesâ
3) Output must land in a predictable place, in a machine-usable format
Pick one canonical output per project (simple is fine):
- Watch:
watch/index.csv+watch/evidence/<watch-id>/*.md|json - Property:
property/index.csv+property/properties/<name>/contacts.csv+property/evidence/...
And each run produces a Run Report linking to what it updated.
4) Background agents should never kill long-running work
Same policy you want globally, but especially here:
- If slow: heartbeat + continue.
- If suspected stuck: checkpoint + ask.
How to capture ânew ideasâ without derailing the flagship
You want an âidea inboxâ per project thatâs easy to append to.
Minimal system that works:
reels/IDEAS.mdwatch/IDEAS.mdproperty/IDEAS.md
Workflow:
- When you have an idea, you drop it in with a date + 1â2 lines.
- Juniperâs job (daily/weekly) is to convert the best ideas into Task Packets and park them in the backlog (not execute immediately).
If you want it even lighter: agree on a prefix you can type anywhere, like:
IDEA(reels): âŚIDEA(watch): âŚ
Then Juniper collects them into the rightIDEAS.mdduring the daily digest/triage.
One important warning (so you donât get âbusywork indexingâ)
Background indexing only pays off if you decide up front what âstructuredâ means.
For Watch, that usually means you define the target fields now (even if incomplete), e.g.:
- brand, model, reference, year, serial (if applicable)
- purchase date, purchase price, currency
- market value estimate + source URL/date
- accessories list
- evidence pointers (files/URLs)
For Property, define:
- property name, address (or internal label), key contacts, emergency procedures
- contracts list (vendor, renewal date, cost)
- evidence pointers
If you donât define this, the agent will âindexâ forever in a way thatâs hard to use later.
Quick clarifiers so I can suggest your first 5 background batches
- For Watch + Property, where is the source material primarily?
- files/folders, email, cloud drive, notes app, something else?
- Roughly how much compute do you have for background runs (can you run 2 overnight without slowing Reels)?
- Do you want background agents to also do web lookups, or only local-file indexing for now?
Answer those, and Iâll propose very concrete âbatch ticketsâ for Rosemary/Holly that will produce reusable artifacts while Reels stays the flagship.
OK, what should w edo first, set up the background tasks or set up the process for the flagship process? I actually have schemas for both the watch and property projects, and they have been implemented in nocoDB. Rosemary for watches and Olive for Reel project, and Holly for property management. I am storing them all in tables in a shared postgresqul dataabse, so that theu all have acces tot he image assets I have imported and stored in an immich server.
Do one small âoperating systemâ step first, then you can run flagship + background in parallel safely.
Given you already have NocoDB schemas + a shared Postgres + Immich, the fastest win is:
Step 1 (do this first): create a unified Run/Output log in Postgres/NocoDB
This directly fixes âwhere did the output go?â and also enables âwere they blocked all night?â
Add one table (name it whatever you want, e.g. agent_runs / run_reports) with fields like:
project(reels / watch / property)agent(Olive / Rosemary / Holly)ticket_id(Trello link/id or internal)status(running|blocked|done)started_at,last_heartbeat_at,finished_atsummary(short text)artifacts(links/paths/Immich IDs)blocker(text)
next_step(one-liner)
Then in NocoDB you can make two views immediately:
- Todayâs Output (status=done, finished today)
- Stale/Blocked (status=running + last_heartbeat too old, or status=blocked)
This becomes your unified âwhat happened?â dashboard without chasing markdown/log files.
Step 2: set the heartbeat + no-kill policy (global)
Rule for Olive/Rosemary/Holly:
- Update
last_heartbeat_at+ 1â2 lines of progress every N minutes (pick 30â60 for overnight). - Never kill slow processesâonly checkpoint + ask if it looks hung.
Step 3: then run in parallel (flagship + background), but batch the background hard
Once Step 1+2 exist, you can do:
- Flagship (Reels / Olive): run continuously toward the next v1 milestone.
- Background (Watch / Rosemary): indexing/enrichment in small batches (e.g. 10 watches/run), writing results into your existing watch tables + logging a run row.
- Background (Property / Holly): file/email inventory + contact/contract extraction batches, writing to property tables + logging a run row.
The key is: background jobs must be quota-based and must always leave a run record, so they canât âdisappearâ for 12 hours.
Why this order
If you start background tasks before the run log + heartbeat policy, youâll get exactly the pain you described: lots of work happening somewhere, and you still wake up unsure if anything moved or if it got stuck.
If you tell me what youâre using as the âunit of progressâ for each project (e.g. Reels = scenes processed; Watch = watches enriched; Property = files indexed / properties completed), I can suggest reasonable heartbeat intervals + batch sizes that wonât spam you but will prevent silent overnight stalls.
Reels = scenes identified and tagged, Watch = watch records enriched (empty fields filled), Property = files indexed, contacts/vendors/contracts identified. I have a very large allocation (Minimax max, 1000 prompts/hr) plan on minimaxM2.5/2.1 that can run basically 24/7, as long as there arent big bursts of activity that can flood the request queue, and I have a local ollama instance with that can rtun small local models like qwen vision, 24/7 also. I beleive thatminimax does have some vision capacity, but I havenmt tested it, and it requirees sending data to cloud servers, which might be insecure/high latency, Local models are slow (sometimes minutes per item), but can be monitored locally and restarted and run continuously. If actually running 24 hr/day, they can process far more items in a week than we could send to a cloud server.
Good metrics â those are clean âunits of progress,â which means you can make the system measurably reliable.
Recommended heartbeat + batch sizing (tuned for slow local + queue-flood avoidance)
The principle is:
⢠Heartbeat is time-based (so you can detect stalls)
⢠Progress is unit-based (so you can see throughput)
⢠Batches are small enough to finish (so nothing runs for 12 hours without a checkpoint)
Hereâs a set of defaults that usually works well when some items can take minutes:
| Project | Unit of progress | Batch size per run | Heartbeat update | âStaleâ threshold (alert) |
| -------- | ---------------- | -------------------------------------- | ------------------------------------ | ------------------------------ |
| Reels | scenes tagged | 200 scenes or 1 reel (whichever first) | every 30 min and every 50 scenes | 2 hours since last heartbeat |
| Watch | records enriched | 10 watches | every 20â30 min and after each watch | 90 min since last heartbeat |
| Property | files indexed | 200 files (or one folder) | every 30â60 min and every 50 files | 2â3 hours since last heartbeat |
Why these numbers:
⢠Theyâre big enough to make real progress, small enough that a failure doesnât waste a whole night.
⢠They prevent a âsilent overnight runâ even if a single item takes 5â10 minutes.
If your local vision pass is really slow (e.g., 3â8 minutes per item), donât increase batch sizeâkeep batches small and let â24/7â do the scaling.
âââ
Avoiding request-queue floods (cloud plan) without giving up 24/7 throughput
Queue floods happen when multiple agents all decide ânow is a good timeâ and burst simultaneously.
Two simple controls that solve most of it:
- Cap concurrency per lane
⢠Flagship (Reels): 1 heavy worker at a time
⢠Background Watch: 1 worker
⢠Background Property: 1 worker
Thatâs already a lot of throughput if they run continuously. - Force micro-batches
⢠Each run processes only its quota and then exits cleanly.
⢠If thereâs more work, it schedules the next micro-batch rather than continuing forever.
This makes load smooth instead of spiky.
âââ
Privacy + vision: a practical split that usually works
Given your concerns (and theyâre valid):
⢠Use local vision for anything image-heavy/private (frames, faces, documents).
⢠Use cloud mainly for text-only web lookup and âlight reasoningâ where youâre not shipping images off-box.
⢠Store only derived metadata in Postgres (scene tags, confidence, links to Immich asset IDs), not raw media in prompts.
That gives you privacy + continuous throughput.
âââ
One extra reliability rule that pays off immediately
Every run must write two things:
- a row in your
agent_runs/Run Report table (status + heartbeat + summary) - a row/update in a project âwork queueâ / âneeds reviewâ list when confidence is low
That prevents âlooks done but is wrongâ from silently contaminating your DB.
Examples:
⢠Reels: âscene tags confidence < X â needs_review=trueâ
⢠Watch: âvaluation source ambiguous â needs_review=trueâ
⢠Property: âcouldnât classify file â needs_review=trueâ
âââ
Next step (concrete)
Since you already have NocoDB + Postgres, the very next thing Iâd do is define one shared Run Report table plus these two views:
⢠Todayâs Output (done runs, with artifact links)
⢠Stale/Blocked (running runs with old heartbeat OR blocked)
Once thatâs in place, Hazel can ânight watchâ by looking at a single view.
If you paste your existing watch/property/reels table names (just the names, not data), I can suggest exactly where the run log should live and what columns to add so it stays minimal but actually useful.
I will share each schema one at a time: 14 tables â NocoDB/PostgreSQL properties: ID, address, nickname, property_type, llc_name, , created_at, updated_at
properties_systems: ID, property_id, system_type, system_name, make_model, install_date, warranty_expiry,
last_serviced, vendor_id, vendors: ID, name, service_type, primary_contact, phone, email, website, address, is_emergency, rate_typical,
vendor_contracts: ID, vendor_id, property_id, contract_type, start_date, end_date, cost, coverage, contract_doc,
neighbors: ID, name, property_address, relationship, phone, email, notes stores_restaurants: ID, name, type, address, phone, website, properties_served,
maintenance_schedule: ID, property_id, task, system_type, frequency, next_due, last_done, vendor_id, estimated_cost,
is_active,
maintenance_log: ID, property_id, task, system_type, vendor_id, service_date, cost, invoice_num, description, photos,
warranty_valid, warranty_expiry emergency_contacts: ID, name, service_type, phone, account_num, properties_covered, is_primary,
access_codes: ID, property_id, location, code, code_type, changed_date, changed_by, is_active
issues: ID, property_id, title, description, system_type, status, priority, reported_date, resolved_date, vendor_id,
estimated_cost, actual_cost cameras: ID, property_id, location, brand, device_name, stream_url, thumbnail_url, motion_alerts, is_active,
sensors: ID, property_id, location, sensor_type, brand, device_id, ha_entity, is_active,
This property schema is a strong âops brainâ already â itâs basically the target you want the background indexing to populate. Two observations jump out:
1) Youâre missing one table that makes âfiles indexedâ real
Right now you have places to attach docs/photos (vendor_contracts.contract_doc, maintenance_log.photos, etc.), but you donât have a canonical place to store:
- âHere is every file/email/attachment we foundâ
- âHere is what we think it relates toâ
- âHere is whether itâs been processed/reviewedâ
So for the Property = files indexed progress metric, you almost certainly want a minimal Documents/Files index table (call it property_documents or just documents) with fields like:
idsource_type(filesystem/email/drive/scan/etc.)source_locator(path/message-id/url)filename,mime_type,size,hashcreated_at,modified_atproperty_id(nullable until classified)
vendor_id/contract_id/issue_id(nullable)doc_type(contract/invoice/manual/photo/permit/etc.)summary(short)extracted_dates(text or json)status(new|processed|needs_review|error)sensitivity(so you can avoid leaking access codes/account numbers into digests)
Without this table, âindexingâ tends to become scattered links and you lose the ability to answer âwhatâs left?â reliably.
2) You also want the Run Report table in the same DB (so NocoDB becomes your dashboard)
Separately from property docs, add a global agent_runs / run_reports table (used by Reels/Watch/Property). Thatâs your âwhat did agents produce today?â view.
Important: since you have access_codes + emergency_contacts.account_num in this schema, make sure the run reports/digests never include raw codes/account numbers. Keep the digest at âX access codes updated for Property Yâ with links into NocoDB, not the secrets themselves.
How Hollyâs background indexing should populate your existing tables
Given your tables, a clean ingestion loop is:
- Create document rows (new
documentstable) for a batch (e.g., 200 files) - For each document:
- classify to a
property_id(or markneeds_review) - extract vendor candidates â upsert into
vendors - if contract/invoice â create/attach
vendor_contractsand/ormaintenance_log - if itâs an issue report â create
issues - if it contains schedules â add to
maintenance_schedule
- classify to a
- Mark document
status=processedand log a run inagent_runs
That gives you steady measurable progress: âfiles indexedâ = count of processed docs.
A very concrete first batch ticket for Holly (low risk, high payoff)
Property Batch 1: âIndex 200 files and only do classification + vendor extractionâ
- Output: 200 new
documentsrows +property_idclassification where possible + vendor candidates invendors - No deep synthesis, no handbook writing yet.
- Anything ambiguous goes to
documents.status=needs_review
This will immediately start building the foundation for the handbook without burning time on narrative output.
If you share the Watch schema next, Iâll do the same: identify the one âmissing glue tableâ (usually an evidence/source table) + propose the first safe enrichment batch that doesnât explode into open-ended research.
for ROsemary: AQ#,McGrath #,PRT Watch List,Status,Date of Purchase,Date Delivered,Yr of Manufacture,Manufacturer,Mfr. Reference
#,Mfr. Serial #,Limited Edition or #,Mfr. Model Name,Movt. M/A,Collection,Nickname,Case Size,Case
Thickness,Crystal,Dial Color,Case Material,Strap Size,Water Resistance,Strap/Bracelet,Bracelet Material,Production
Year (Circa),Condition,Seller,Seller Item #,Description,Complications,Box and Papers,Case Description,Dial
Description,Bezel Description,Movement Description,Strap/Bracelet Description,Purchased From,Purchase Price in
original currency w/o tax,Total Purchase Price USD w/tax+shipping,Sold for,Dates Serviced,Serviced by,Links,Unnamed:
43
Nice â thatâs a very âreal collector spreadsheetâ schema. Itâs a good starting point, but for agent-driven enrichment youâll want to add (or emulate) two missing concepts that arenât represented well in a single wide table:
1) You need an Evidence/Source table (like the âdocumentsâ table I suggested for Property)
Right now you have a single Links column, which is where accuracy goes to die. Agents will overwrite/guess unless you force an evidence trail.
Add a table (or equivalent structure) like watch_sources / watch_evidence:
idwatch_id(FK)source_type(press, manufacturer site, listing, receipt, forum, auction result, etc.)url(or local file pointer)retrieved_atexcerpt / key facts(short)fields_supported(e.g., âcase sizeâ, âmovementâ, âMSRPâ, âproduction yearâ)confidencearchived_copy(optional: where you saved the page/PDF locally)
Rule for Rosemary: only fill/modify a watch field if thereâs at least one evidence row supporting it.
That single rule massively reduces hallucinated enrichment.
2) You need time-series tables for âvalueâ + âserviceâ (donât store history in one cell)
You have:
- purchase price fields (good)
- âSold forâ (single value)
- âDates Serviced / Serviced byâ (which will become messy)
Agents (and humans) will inevitably find multiple values over time. So create separate tables:
watch_transactions
- purchase/sale events (date, price, currency, seller/buyer, fees, notes, receipt doc link)
watch_valuations
watch_id,valuation_date,value_usd,source_url,method(market comp / insurance / auction),notes
watch_service_events
watch_id,service_date,serviced_by,notes,docs/photos
This prevents your main watch table from getting overwritten and lets you âtrack valueâ properly.
Keep your current wide table â but treat it as the âCore Watch Recordâ
Your existing fields are great for the core record:
- Identity: Manufacturer, Reference #, Serial #, Model Name, Limited Edition #
- Physical specs: case size/thickness/material, dial color, crystal, WR, bracelet, etc.
- Narrative: description fields (case/dial/bezel/movement/strap) are useful, but should be marked as âhuman-writtenâ vs âagent extractedâ
Two small improvements that help agents a lot:
- Add
watch_id(stable internal ID) if you donât already have one - Add
needs_review+last_verified_atso agents can propose changes without âfinalizingâ them
First safe background batch for Rosemary (high leverage, low risk)
Watch Batch 1: Evidence-first enrichment for 10 watches
For each watch:
- Create 2â3
watch_sourcesrows (manufacturer page + one reputable secondary + one market listing/auction if available) - Fill only a short whitelist of fields that are usually easy to verify:
- year of manufacture (if supported)
- movement type (M/A)
- case size
- case material
- water resistance
- If conflicting sources: set
needs_review=trueand log the conflict in the sources.
This produces clean, compounding progress without polluting the dataset.
If you tell me how Rosemary currently stores this in NocoDB (one big watches table only vs already split tables), I can recommend the minimum extra tables/fields to add so the enrichment pipeline stays accurate and reviewable (and doesnât rewrite your collector notes).
OK, I have a couple setup questions. Right now, I have three Bases in NocoDB, one for each project. Should I aggregate them into one Base with different tables, so that the RunReport table lives in the same BAse?
You donât have to merge the bases â the key is just: there should be exactly one canonical RunReport table somewhere that all agents can write to and Hazel can read from.
A few practical tradeoffs:
If all 3 bases are backed by the same Postgres database (sounds like they are)
Then âBaseâ is mostly a NocoDB organization/UI choice. In that case, Iâd do this:
- Keep your three project bases if you like the separation.
- Create a small Ops/Meta schema in Postgres (conceptually:
ops.run_reports,ops.artifacts, maybeops.work_queuelater). - Add a fourth NocoDB base (or one shared base) that points at the same Postgres DB and shows just the Ops tables + dashboards (Todayâs Output, Stale/Blocked).
That gives you one place to look for âwhat happened today?â without mixing all project tables together.
Reasons not to merge everything into one base
- Your Property base contains very sensitive stuff (
access_codes, account numbers, etc.). Keeping it isolated reduces the chance you accidentally surface secrets in global dashboards or share links/screenshots. - Operationally, you donât actually need cross-project joins to make RunReports useful.
When merging into one base is worth it
- You want a single NocoDB âhomeâ where you can build cross-project dashboards easily.
- You want consistent permissions/roles across all project tables.
- Youâre disciplined about not exposing sensitive tables in global views.
The most important point (for agents)
Agents donât care about âwhich NocoDB baseâ â they care where the Postgres table lives. So decide first:
- âRunReports lives in Postgres in schema Xâ
Then you can surface it in NocoDB however is most comfortable (one base or multiple).
If you answer one thing, I can give you a clear recommendation: are the 3 NocoDB bases three separate database connections, or are they all pointing at the same Postgres DB (just different groups/tables)?
they are all in ths same Postgres DB with different groups.
Perfect â if theyâre all the same Postgres DB, then you definitely do not need to âstart overâ or merge for technical reasons. This becomes a pure UX / permissioning choice.
My recommendation: keep the 3 project bases, add one small âOpsâ base (or shared group)
- Keep Reels / Watch / Property as separate bases if thatâs helping you stay mentally organized.
- Create
ops.run_reports(and laterops.artifacts,ops.work_queueif you want). - In NocoDB, expose those ops tables in a dedicated Ops base (or however you prefer to surface them).
- Hazel uses the Ops base as the âsingle pane of glassâ (Todayâs Output, Stale/Blocked).
This gives you:
- one canonical place to see progress across everything
- no forced UI clutter inside each project base
- easier control over sensitive tables (Propertyâs
access_codesetc.)
When it does make sense to merge into one base
Only if you really want one unified dashboard that mixes project tables + ops tables in one place, and youâre confident your table permissions/views wonât accidentally surface secrets. Itâs doable â just not required.
Key point
Even if you keep 3 bases, the RunReport table is still âsharedâ because itâs in the same Postgres DB. Agents will write to the same place either way.
If you want, tell me what youâd prefer day-to-day:
- Option A: âI want one global Ops dashboard baseâ
- Option B: âI want to see RunReports inside each project base tooâ
âŚand Iâll suggest the cleanest layout (and the minimal columns/views that make the dashboard actually useful without leaking anything sensitive).
yes one Ops shared ops base is good. can you gie me the prompt I can give my main agent to create the Ops table and link it to the other bases?
Hereâs a copy/paste prompt for Juniper (or your âmain agentâ) thatâs specific enough to execute, but still lets it adapt to whatever NocoDB UI options you have.
Youâll notice Iâm using a prefix (ops_âŚ) instead of a Postgres schema (ops.âŚ) because NocoDB setups vary on schema support. If you know your NocoDB happily works with non-public schemas, you can swap to an ops schema later.
Prompt: âCreate Ops base + Run Reports table + views + link into other basesâ
You are Juniper. Task: create an "Ops" shared base in NocoDB for cross-project visibility, backed by the existing shared Postgres DB (same DB used by Reels/Watch/Property).
GOALS
1) Create a new NocoDB Base named: "Ops"
- It must connect to the SAME Postgres database used by the other bases.
2) Create a table in Postgres (via NocoDB table-creation UI if possible) named:
ops_run_reports
Columns (use closest available NocoDB types; if json isnât supported, use Long Text):
- id (primary key; auto/uuid ok)
- project (Single select or text) -- reels/watch/property/ops/other
- agent (text) -- Olive/Rosemary/Holly/Hazel/Juniper
- ticket_ref (text) -- Trello URL/ID or internal ticket
- status (Single select) -- running | blocked | done
- started_at (date-time)
- last_heartbeat_at (date-time)
- finished_at (date-time, nullable)
- progress_current (number, nullable) -- e.g. scenes processed / watches enriched / files indexed
- progress_total (number, nullable)
- progress_unit (text) -- "scenes" / "watches" / "files"
- summary (long text) -- 3â7 bullet summary of what happened
- artifacts (long text or json) -- links/paths/Immich IDs
- blocker (long text, nullable)
- next_step (text, nullable)
Data hygiene / safety:
- Never store secrets in this table (no access codes, account numbers, raw credentials).
- Summaries can link to NocoDB rows instead of pasting sensitive content.
3) Create two saved views in the Ops base:
A) "Todayâs Output"
- filter: status = done AND finished_at is today
- sort: finished_at desc
B) "Stale / Blocked"
- filter: status = blocked OR (status = running AND last_heartbeat_at older than 2 hours)
- sort: last_heartbeat_at asc
4) Link this table into each project base so itâs easy to access everywhere:
- In each of the 3 project bases (Reels / Watch / Property), create an "Ops" group.
- Add/import the existing table ops_run_reports into that base/group (read-only usage is fine).
- If NocoDB does not support surfacing the same table in multiple bases, then:
- keep ops_run_reports only in the Ops base, and
- create a prominently named shortcut/shared view link in each base (whatever NocoDB supports).
5) Write a short âHow to use ops_run_reportsâ note (in a NocoDB description field or a small doc) with the lifecycle:
- create row at start (status=running, started_at, last_heartbeat_at)
- update last_heartbeat_at + progress every 30â60 min (or every 50 units)
- on blocker: status=blocked + blocker + next_step
- on success: status=done + finished_at + summary + artifacts
SUCCESS CRITERIA
- I can open Ops base and see Todayâs Output and Stale/Blocked views.
- From each project base, I can easily access the same run report info (either the table itself or a link to the Ops base view).
- No sensitive property secrets are present in ops_run_reports.
If you want, I can also give you a âRosemary/Olive/Holly contractâ prompt that forces them to write a RunReport row per batch (and to heartbeat) so the table actually gets used consistently.
Can you wrtie a prompt ffor juniper to add this table to the property dataabse? How Hollyâs background indexing should populate your existing tables
Given your tables, a clean ingestion loop is:
- Create document rows (new documents table) for a batch (e.g., 200 files)
- For each document:
classify to a property_id (or mark needs_review)
extract vendor candidates â upsert into vendors
if contract/invoice â create/attach vendor_contracts and/or maintenance_log
if itâs an issue report â create issues
if it contains schedules â add to maintenance_schedule
3) Mark document status=processed and log a run in agent_runs
That gives you steady measurable progress: âfiles indexedâ = count of processed docs.
Hereâs a copy/paste prompt for Juniper that turns the âProperty indexing loopâ into concrete database tables + views + an execution contract for Holly. Iâm keeping it NocoDB-friendly (types can be adjusted to whatever the UI supports).
Prompt for Juniper: add Property Documents table + define ingestion loop for Holly
You are Juniper. Task: implement the Property âdocuments indexingâ foundation inside the existing shared Postgres/NocoDB setup, so Holly can do background indexing in bounded batches and we can measure progress as âfiles indexedâ.
CONTEXT
- We have existing Property tables: properties, vendors, vendor_contracts, maintenance_schedule, maintenance_log, issues, etc.
- We have (or will have) a shared ops_run_reports / agent_runs table for logging runs (project=property).
- We want a clean ingestion loop:
1) create document rows for a batch (e.g., 200 files)
2) classify + extract entities and upsert into existing tables
3) mark doc processed and log a run
GOALS
1) Create a new table in the Property domain called:
property_documents
(If schema support is messy, keep it in the same schema as the other property tables. Use a clear prefix.)
Required columns (use closest NocoDB types; if JSON isnât supported, use Long Text):
- id (primary key)
- source_type (single select): filesystem | email | drive | scan | other
- source_locator (text): path / message-id / url (unique-ish)
- filename (text)
- mime_type (text)
- size_bytes (number, optional)
- checksum_hash (text, optional but recommended for dedupe)
- created_at (date-time)
- modified_at (date-time, optional)
- property_id (FK to properties, nullable until classified)
- vendor_id (FK to vendors, nullable)
- contract_id (FK to vendor_contracts, nullable)
- issue_id (FK to issues, nullable)
- maintenance_log_id (FK to maintenance_log, nullable)
- doc_type (single select): contract | invoice | manual | receipt | permit | photo | email | other
- status (single select): new | processing | processed | needs_review | error
- sensitivity (single select): normal | sensitive
- summary (long text, optional)
- extracted_entities (long text or json, optional) -- e.g. vendor name, phone, dates, costs
- notes (long text, optional)
Data hygiene:
- Do NOT copy raw access codes/account numbers into summary fields.
- Mark docs containing secrets as sensitivity=sensitive and keep summaries high-level.
2) Create the key saved views in NocoDB (Property base and/or Ops base):
A) "Docs: Unprocessed"
- status in (new, needs_review, error)
B) "Docs: Needs Review"
- status = needs_review
C) "Docs: Processed Today"
- status = processed AND (processed timestamp if available OR updated_at is today)
D) "Index Progress"
- show counts of processed docs by property_id (if NocoDB supports grouping/rollups)
NOTE: If property_documents doesnât have an explicit processed_at field, add:
- processed_at (date-time, nullable)
so âprocessed todayâ is reliable.
3) Define the exact ingestion contract for Holly (write it into a short runbook doc, or into a description field):
- Batch size: create document rows for exactly 200 files per run (or 60 minutes max).
- For each doc row:
a) classify property_id (or set needs_review with a short reason)
b) extract vendor candidates and upsert into vendors (dedupe by name+phone/email)
c) if contract/invoice:
- create vendor_contracts and link contract_doc to the document (either by storing property_documents.id in vendor_contracts.contract_doc, or by storing a URL/locator)
- optionally create maintenance_log entry if itâs a service invoice
d) if issue report: create issues row and link issue_id
e) if schedule info: create or update maintenance_schedule
f) set property_documents.status=processed and processed_at=now
- If extraction is ambiguous: status=needs_review and write a short note (no secrets).
4) Connect this to run logging (ops_run_reports / agent_runs):
- Every Holly indexing run must create/update a run report row with:
project=property, agent=Holly, status, last_heartbeat_at, progress_current (# docs processed), progress_unit=files.
- On completion: status=done with a short summary + links to the âDocs: Processed Todayâ view.
SUCCESS CRITERIA
- property_documents exists and can store a batch of discovered files.
- We can measure progress as COUNT(property_documents WHERE status=processed).
- Holly can run repeatable batches without duplicating the same file endlessly (checksum/source_locator helps).
- Sensitive info is not leaking into run reports/digests.
If you want, I can also write the Holly execution prompt (the one she runs each time) that strictly follows this contract: âprocess 200 docs, heartbeat every 30â60 minutes, never kill slow work, always end by marking docs + updating run report.â
and is there a verion of this field for each Base? (URLS for websearches, reel file names )
Yes â you want the same pattern in every base, just adapted to the kind of âraw inputâ that project consumes.
Think of it as: every project needs a table that answers âwhat source material did we look at, and what did we extract from it?â
Option 1 (recommended): one âEvidence/Sourcesâ table per project base
This keeps things clean and avoids mixing sensitive property data with everything else.
Property (you already have the right idea)
property_documents
source_locator= file path / email message-id / drive URL- plus links to
property_id,vendor_id, etc.
Watch
watch_sources (or watch_documents)
source_locator= URL, receipt filename, PDF path, article link- FK:
watch_id - fields:
source_type,retrieved_at,summary,fields_supported,confidence,status
This is the âversion of the fieldâ for web searches: you store the URL + what it supports, not just a blob of links.
Reels
Usually you split âinputsâ into two levels:
media_assets(orreel_files)
source_locator= reel filename, storage path, or Immich asset id- FK:
reel_id/tape_id - metadata: duration, fps, resolution, etc.
scene_segments(if you donât already have it)
- references the asset + start/end timecodes
- extracted tags/people/events live here or in linked tables
So for Reels, your âsource locatorâ equivalent is typically file/asset id + timecodes, not URLs.
Option 2: one global table for all projects (only if you really want it)
You can do a single ops_sources / ops_documents table with columns like:
project(reels/watch/property)entity_type(watch/property/scene/etc.)entity_id(the FK id as text)source_type,source_locator,retrieved_at,status,summary,sensitivity
This is nice for unified dashboards, but Iâd only do it if youâre confident you wonât accidentally surface sensitive Property docs in global views.
The âcommon columnsâ Iâd standardize across all bases
No matter what you call the table, keep these consistent:
source_type(url/file/email/immich/etc.)source_locator(the stable pointer: URL or filename or asset id)retrieved_at/created_atstatus(new/processed/needs_review/error)
summary(short)sensitivity(normal/sensitive)
Then add project-specific FKs (watch_id, property_id, scene_id).
That consistency is what lets your agents run the same ingestion logic everywhere.
If you tell me what your Reels base tables are called (just table names), I can suggest the exact âReels equivalentâ of property_documents in your current schema (asset table vs scene table vs tags table).
Watch Database, Propterty Handbook, Reel Database
Got it â those are your base names. Keep them as-is.
What you want next is a consistent âSources/Evidenceâ table in each base, named for the project:
- Watch Database:
watch_sources(URLs, receipts/PDFs, listings, press, etc. â linked towatch_id) - Property Handbook:
property_documents(files/emails/contracts/invoices/manuals â linked toproperty_id,vendor_id, etc.) - Reel Database:
reel_assets(ormedia_assets) + (usually)scene_segments
For Reels, the âsource locatorâ equivalent is typically:
immich_asset_id(or whatever stable media ID you use)original_filename/ storage path- and for scenes:
start_timecode+end_timecode(or frame numbers)
Quick question so I can be precise for Reels
Can you paste the table names currently inside your Reel Database base (just names, no data)?
Example format: reels, scenes, people, events, tags, scene_tags, âŚ
Once I see that list, Iâll tell you the minimal change to get a proper reel_assets/media_assets layer (without reshaping your whole schema).
Reels, people, events, locations, scenes, face_frames, batches (for processing), tags, scene_tags
That Reels base is already very close to the âevidence patternâ you want â you basically already have it. The key is to make sure you have one table that cleanly represents the raw input media, with a stable âsource locatorâ, and then everything else hangs off it.
In your Reels base, the âdocuments/sourcesâ equivalent is: reels
Treat reels as your media asset index (the thing you are processing), and make sure it has a few âsource locatorâ fields (names can vary):
Minimum fields Iâd want on reels:
source_type(immich / filesystem / other)source_locator(Immich asset id, or file path) â this is the âdocument URLâ equivalentoriginal_filename(or file label)- optional but very useful:
duration,fps,resolution,checksum_hash status(new|processing|processed|needs_review|error)ingested_at/processed_at
If each physical reel can produce multiple files (raw scan, stabilized, proxy), then add a child table like reel_media_assets â but if itâs 1:1, keeping it on reels is fine.
scenes is your derived-unit table (where âscenes taggedâ lives)
Minimum fields Iâd want on scenes:
reel_id(FK)start_timecode/end_timecode(or frame start/end)description(optional)needs_review+confidencestatus(new|tagged|reviewed)
Then scene_tags + tags already gives you the flexible tagging system.
face_frames is also âevidenceâ, but at a different granularity
That table should link back to either:
scene_id(best), and/orreel_id+ frame timecode
and store the derived face data (bbox, embedding id, etc.).
batches is your processing ledger (and should connect to RunReports)
This is perfect for joining your Ops visibility to real work.
this is our current schema, we have not yet implemented the database, can you modify it to add the schema that will allow us to track jobs? Looing at the whole schema, is there anything e;se we missed?
Pattern:
- each Olive run creates a
batchesrow - that run also creates/updates an
ops_run_reportsrow batches.run_report_id(orops_run_report_id) is a great link if you add it
What to do next (minimal change)
- Ensure
reelshas a stablesource_locator(Immich asset id or path) +status - Ensure
sceneshas timecodes +status/needs_review - Optionally link
batchesâops_run_reportsso your digest can click straight to âwhat batch did this run produce?â
If you want, I can write a Juniper prompt to do the exact minimal schema edits for Reels (add missing columns only, no refactors), based on what fields you already have on reels and scenes. To do that, paste the column list for reels and scenes (just names, no data).
Yep â if you havenât implemented it yet, this is the perfect moment to add job tracking + provenance cleanly. With your current Reels schema:
reels, scenes, people, events, locations, tags, scene_tags, face_frames, batches
âŚyouâre missing two âsystem-levelâ capabilities that make agent work reliable:
- Job/run tracking (so you can see whatâs running, stalled, done, failed, with progress + heartbeats)
- Provenance / review trail (so you can tell what was added by an agent vs a human, and what still needs review)
Below is a modified schema that keeps your existing tables, upgrades batches, and adds the minimal missing tables.
A) Job tracking: add jobs + convert/repurpose batches into job_runs
1) jobs (high-level âwhat work is this?â)
One row per logical job (e.g., âextract scenes for Reel 012â, âtag 200 scenesâ, âextract faces for Reel 012â).
jobs
idproject(default"reels"; useful if you later reuse the same pattern)job_type(single select):import_reel | detect_scenes | tag_scenes | extract_face_frames | cluster_faces | link_faces_to_people | generate_thumbnails | qc_review | exportstatus(single select):queued | running | blocked | done | failed | cancelledpriority(number)created_atstarted_atlast_heartbeat_atfinished_atrequested_by(text: user/agent)assigned_agent(text: Olive/etc.)progress_current(number)progress_total(number, nullable)progress_unit(text: scenes/reels/frames/etc.)summary(long text)blocker(long text, nullable)
error(long text, nullable)params(json/long text) â store any knobs like thresholds, model name, input selectors, etc.
Why: this is the canonical âjob stateâ that powers dashboards and alerts.
2) job_runs (every attempt/execution of a job)
This is what your current batches table wants to be. If you like the name batches, keep it â just give it these fields and treat it as ârunsâ.
job_runs (or rename batches â job_runs)
idjob_id(FK â jobs)attempt(number; 1,2,3âŚ)status(queued/running/blocked/done/failed)started_at,last_heartbeat_at,finished_atrunner(text: Olive/local pipeline/etc.)log(long text) orlog_urlmetrics(json/long text) â e.g. âscenes_created=200, faces_extracted=1200â
error(long text, nullable)
Why: youâll want retries without losing history, and youâll want âwhat happened last night?â even if the job ultimately failed.
3) job_items (optional but very powerful for âbatchingâ)
If you want reliable batch processing (200 scenes at a time), add this table:
job_items
idjob_identity_type(single select):reel | scene | face_frameentity_id(text/number depending on your IDs)status(queued|done|error|needs_review)notes(text)
This is what prevents âdouble-processingâ and makes progress measurable and restartable.
B) Provenance/review trail: upgrade scene_tags (and optionally add annotations)
Right now scene_tags likely just has scene_id, tag_id. Add provenance fields so you can trust the data:
scene_tags (add columns)
added_by(text: username/agent name)source(single select):agent | human | importconfidence(number 0â1 or 0â100, nullable)created_atneeds_review(boolean)
Do the same pattern anywhere an agent is âwriting factsâ.
Optional: scene_notes / annotations
If you want family members to add context without messing with tags:
scene_notes
idscene_idauthor(text)note(long text)created_atsource(human|agent)
This is a huge part of making the system âfamily usableâ.
C) Reels âsource locatorâ: make sure reels can point to the actual media
To make reels your âevidence tableâ, ensure reels has:
source_type(immich/filesystem)source_locator(Immich asset id or path)
original_filenamestatus+processed_at- optional:
duration,fps,resolution,checksum_hash
Thatâs the Reels equivalent of âURLs for web searchesâ.
D) Anything else youâre likely missing?
Two common âwe forgot thisâ items for this kind of system:
-
Users / contributors
If family members will edit/add info, youâll want at least a lightweightuserstable or anauthorfield everywhere (notes/tags/edits). -
Many-to-many links for people/events/locations (if you want them first-class)
You currently havetags+scene_tagsand separatepeople/events/locations.
Decide one of these approaches:
- Unify: make people/events/locations be âtag typesâ (simpler)
- Keep separate: add join tables like
scene_people,scene_events,scene_locations(cleaner semantics)
- Keep separate: add join tables like
If you donât decide, youâll end up duplicating concepts.
Quick check before I tailor this tighter
When you say ânot yet implementedâ, do you mean:
- âtables not created anywhere yetâ (we can redesign freely), or
- âtables exist in NocoDB but agents havenât started using themâ?
If you answer that, I can give you a minimal diff (exact tables/fields to add) vs a âclean redesignâ version.
{"reels":{"id":"integer_auto","r eel_code":"text_unique","title": "text","format":"text","film_stock":"text","date_acquired":"date","condition":"text","duration_m inutes":"decimal","notes":"text"
,"created_at":"datetime"},"peopl e":{"id":"integer_auto","name":" text","aliases":"text","birth_year":"integer","relationship":"text","immich_person_id":"text","i mmich_person_name":"text","notes ":"text","created_at":"datetime" },"events":{"id":"integer_auto","name":"text","event_type":"text","start_date":"date","end_date":"date","location":"text","people_involved":"link_people_multiselect","description":"text","crea ted_at":"datetime"},"locations": {"id":"integer_auto","name":"text","location_type":"text","notes ":"text","created_at":"datetime" },"tags":{"id":"integer_auto","name":"text_unique","category":"text","color":"text","description":"text","usage_count":"integer","created_at":"datetime"},"scene s":{"id":"integer_auto","reel_id ":"link_reels","reel":"text","timecode":"decimal","scene_key":"t ext_unique","duration_s":"decimal","event_id":"link_events","location_id":"link_locations","scen e_tags":"link_tags_multiselect","people_count":"integer","people_identified":"link_people_multis elect","raw_model_response":"tex t","batch_id":"integer","status" :"text","created_at":"datetime"} ,"face_frames":{"id":"integer_auto","scene_id":"link_scenes","scene_key":"text","person_index":" integer","person_id":"link_people","person_description":"text"," frame_path":"text","frame_timecode":"decimal","review_status":"text","immich_asset_id":"text","immich_person_id":"text","created at":"datetime"},"batches":{"id":"integer_auto","start_index":"i
nteger","end_index":"integer","s cenes_count":"integer","frames_extracted":"integer","status":"tet","html_path":"text","started at":"datetime","completed_at":"d atetime","error_log":"text"}}
This is a solid starting point â youâve got the core entities right. The main gaps (given your âagents run 24/7â plan) are:
- Real job tracking (queued/running/blocked/done + heartbeats + progress + retries)
- Provenance on many-to-many links (who/what added a person/tag/event to a scene, with confidence + review status)
- A stable âmedia source locatorâ on reels/scenes (Immich asset id / file path + timecodes)
Below is a âminimal but correctâ upgrade that keeps your existing tables and adds what you need.
1) Add job tracking tables (recommended even if you keep batches)
Right now batches is a range tracker, not a general job system. Keep it if you like it, but add jobs + job_runs so you can track all types of work.
New: jobs (one row per logical job)
"jobs": {
"id": "integer_auto",
"project": "text_default_reels",
"job_type": "text",
"status": "text",
"priority": "integer",
"target_type": "text",
"target_id": "integer_nullable",
"params": "text_long",
"progress_current": "integer",
"progress_total": "integer_nullable",
"progress_unit": "text",
"created_at": "datetime",
"started_at": "datetime_nullable",
"last_heartbeat_at": "datetime_nullable",
"finished_at": "datetime_nullable",
"assigned_agent": "text_nullable",
"summary": "text_long_nullable",
"blocker": "text_long_nullable",
"error": "text_long_nullable"
}
Suggested job_type values (start small):
detect_scenes,tag_scenes,extract_face_frames,link_faces_to_people,qc_review,import_reel
New: job_runs (each attempt/run of a job)
"job_runs": {
"id": "integer_auto",
"job_id": "link_jobs",
"attempt": "integer",
"status": "text",
"runner": "text_nullable",
"started_at": "datetime",
"last_heartbeat_at": "datetime_nullable",
"finished_at": "datetime_nullable",
"log": "text_long_nullable",
"metrics": "text_long_nullable",
"error": "text_long_nullable",
"ops_run_report_id": "integer_nullable"
}
That last field (ops_run_report_id) is optional, but itâs the clean bridge to your shared Ops base ops_run_reports table.
Optional but powerful: job_items
If you want true âbatch processingâ (no duplicates, restartable), add:
"job_items": {
"id": "integer_auto",
"job_id": "link_jobs",
"entity_type": "text",
"entity_id": "integer",
"status": "text",
"notes": "text_nullable",
"created_at": "datetime"
}
2) Fix Reels/Scenes to support real media linking + time ranges
Your scenes.timecode is a single point â you really want a start/end range.
Modify: reels (add source locator)
"reels": {
"id": "integer_auto",
"reel_code": "text_unique",
"title": "text",
"format": "text",
"film_stock": "text",
"date_acquired": "date",
"condition": "text",
"duration_minutes": "decimal",
"notes": "text",
"source_type": "text_nullable",
"source_locator": "text_nullable",
"immich_asset_id": "text_nullable",
"original_filename": "text_nullable",
"status": "text_nullable",
"created_at": "datetime"
}
Modify: scenes (start/end timecode + job linkage)
"scenes": {
"id": "integer_auto",
"reel_id": "link_reels",
"scene_key": "text_unique",
"start_timecode": "decimal",
"end_timecode": "decimal",
"duration_s": "decimal",
"event_id": "link_events_nullable",
"location_id": "link_locations_nullable",
"status": "text",
"needs_review": "boolean",
"confidence": "decimal_nullable",
"raw_model_response": "text_long_nullable",
"created_at": "datetime",
"job_id": "link_jobs_nullable"
}
(You can keep batch_id if you want, but Iâd rather link to job_id / job_run_id so it works across all processing types.)
3) Add provenance: replace multiselect links with explicit join tables
NocoDB âlink multiselectâ is convenient, but it hides the join table â you canât easily store confidence/added_by/source/created_at on each link. Since family members will edit, Iâd make these explicit now.
New: scene_people (instead of scenes.people_identified)
"scene_people": {
"id": "integer_auto",
"scene_id": "link_scenes",
"person_id": "link_people",
"source": "text",
"added_by": "text_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
}
New: scene_tags (instead of scenes.scene_tags multiselect)
"scene_tags": {
"id": "integer_auto",
"scene_id": "link_scenes",
"tag_id": "link_tags",
"source": "text",
"added_by": "text_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
}
You already mentioned scene_tags in your table list earlier â this is the version you want.
Do the same for events if you care about provenance:
event_people(event â people)
4) Face frames: add bounding box + link to job run
Your face_frames is good. Two additions make it much more useful:
- bounding box (
x,y,w,h) so you can re-render crops reliably - link to the run that generated it
"face_frames": {
"id": "integer_auto",
"scene_id": "link_scenes",
"person_index": "integer",
"person_id": "link_people_nullable",
"person_description": "text_nullable",
"frame_path": "text_nullable",
"frame_timecode": "decimal",
"bbox_x": "decimal_nullable",
"bbox_y": "decimal_nullable",
"bbox_w": "decimal_nullable",
"bbox_h": "decimal_nullable",
"review_status": "text",
"immich_asset_id": "text_nullable",
"immich_person_id": "text_nullable",
"job_run_id": "link_job_runs_nullable",
"created_at": "datetime"
}
5) One more âdid we miss anything?â item: contributors
If family members will add/correct info, add a tiny users / contributors table, or at least store added_by as free text consistently. Otherwise youâll lose track of who said what.
If you want the minimal minimal change set
If you donât want to refactor links yet, do just these first:
- Add
jobs+job_runs - Add
reels.source_locator/immich_asset_id - Change scenes to
start_timecode/end_timecode - Add
ops_run_report_idlink injob_runs
That alone gives you job tracking + visibility without touching tagging.
If you confirm whether youâre okay replacing the multiselect links with explicit join tables (scene_people, scene_tags), I can rewrite your whole schema cleanly in the same JSON style you used, with consistent field names and statuses.
tables not created anywhere yetâ (we can redesign freely)
Great â if nothing exists yet, you should redesign now so you donât fight NocoDBâs âhidden join tablesâ later.
Below is a clean v1 schema for the Reel Database that includes:
- Job tracking (
jobs,job_runs,job_items,artifacts) - Provenance + reviewability for tags/people (
scene_tags,scene_people,scene_notes) - Proper media linking (
reel_media_assetsso one physical reel can have multiple digital files/proxies) - Keeps your existing entity model (
reels/people/events/locations/tags/scenes/face_frames)
You can copy this as the new âsource of truthâ for what to implement in NocoDB/Postgres.
Reel Database (v1) â redesigned schema (tables + key fields)
{
"contributors": {
"id": "integer_auto",
"display_name": "text",
"type": "select(human,agent)",
"notes": "text_nullable",
"created_at": "datetime"
},
"reels": {
"id": "integer_auto",
"reel_code": "text_unique",
"title": "text_nullable",
"format": "text_nullable",
"film_stock": "text_nullable",
"date_acquired": "date_nullable",
"condition": "text_nullable",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"reel_media_assets": {
"id": "integer_auto",
"reel_id": "link_reels",
"asset_role": "select(raw_scan,stabilized,proxy,other)",
"source_type": "select(immich,filesystem,other)",
"source_locator": "text",
"immich_asset_id": "text_nullable",
"original_filename": "text_nullable",
"duration_s": "decimal_nullable",
"fps": "decimal_nullable",
"width": "integer_nullable",
"height": "integer_nullable",
"checksum_hash": "text_nullable",
"status": "select(new,processing,processed,needs_review,error)",
"ingested_at": "datetime_nullable",
"processed_at": "datetime_nullable",
"created_at": "datetime"
},
"people": {
"id": "integer_auto",
"name": "text",
"aliases": "text_long_nullable",
"birth_year": "integer_nullable",
"relationship": "text_nullable",
"immich_person_id": "text_nullable",
"immich_person_name": "text_nullable",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"events": {
"id": "integer_auto",
"name": "text",
"event_type": "text_nullable",
"start_date": "date_nullable",
"end_date": "date_nullable",
"description": "text_long_nullable",
"created_at": "datetime"
},
"locations": {
"id": "integer_auto",
"name": "text",
"location_type": "text_nullable",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"tags": {
"id": "integer_auto",
"name": "text_unique",
"category": "text_nullable",
"color": "text_nullable",
"description": "text_long_nullable",
"created_at": "datetime"
},
"scenes": {
"id": "integer_auto",
"scene_key": "text_unique",
"reel_media_asset_id": "link_reel_media_assets",
"scene_index": "integer_nullable",
"start_timecode": "decimal",
"end_timecode": "decimal",
"duration_s": "decimal_nullable",
"event_id": "link_events_nullable",
"location_id": "link_locations_nullable",
"status": "select(new,tagged,reviewed,needs_review,error)",
"confidence": "decimal_nullable",
"raw_model_response": "text_long_nullable",
"created_at": "datetime"
},
"scene_people": {
"id": "integer_auto",
"scene_id": "link_scenes",
"person_id": "link_people",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
},
"scene_tags": {
"id": "integer_auto",
"scene_id": "link_scenes",
"tag_id": "link_tags",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
},
"scene_notes": {
"id": "integer_auto",
"scene_id": "link_scenes",
"author_contributor_id": "link_contributors_nullable",
"note": "text_long",
"created_at": "datetime"
},
"face_frames": {
"id": "integer_auto",
"scene_id": "link_scenes",
"reel_media_asset_id": "link_reel_media_assets",
"frame_timecode": "decimal",
"frame_path": "text_nullable",
"immich_asset_id": "text_nullable",
"bbox_x": "decimal_nullable",
"bbox_y": "decimal_nullable",
"bbox_w": "decimal_nullable",
"bbox_h": "decimal_nullable",
"person_id": "link_people_nullable",
"person_index": "integer_nullable",
"person_description": "text_long_nullable",
"review_status": "select(new,matched,needs_review,rejected)",
"created_at": "datetime"
},
"jobs": {
"id": "integer_auto",
"job_type": "select(import_reel,detect_scenes,tag_scenes,extract_face_frames,link_faces_to_people,qc_review,export)",
"status": "select(queued,running,blocked,done,failed,cancelled)",
"priority": "integer",
"target_type": "select(reel,reel_media_asset,scene,other)",
"target_id": "integer_nullable",
"params": "text_long_nullable",
"progress_current": "integer",
"progress_total": "integer_nullable",
"progress_unit": "text_nullable",
"created_at": "datetime",
"started_at": "datetime_nullable",
"last_heartbeat_at": "datetime_nullable",
"finished_at": "datetime_nullable",
"assigned_agent": "text_nullable",
"summary": "text_long_nullable",
"blocker": "text_long_nullable",
"error": "text_long_nullable"
},
"job_runs": {
"id": "integer_auto",
"job_id": "link_jobs",
"attempt": "integer",
"status": "select(running,blocked,done,failed)",
"runner": "text_nullable",
"started_at": "datetime",
"last_heartbeat_at": "datetime_nullable",
"finished_at": "datetime_nullable",
"log": "text_long_nullable",
"metrics": "text_long_nullable",
"error": "text_long_nullable",
"ops_run_report_id": "integer_nullable"
},
"job_items": {
"id": "integer_auto",
"job_id": "link_jobs",
"entity_type": "select(reel_media_asset,scene,face_frame,other)",
"entity_id": "integer",
"status": "select(queued,done,needs_review,error)",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"artifacts": {
"id": "integer_auto",
"job_run_id": "link_job_runs",
"artifact_type": "select(html_report,csv_export,thumbnail,other)",
"uri": "text",
"notes": "text_long_nullable",
"created_at": "datetime"
}
}
What this fixes (the âdid we miss anything?â answer)
- You can now track: queued â running â heartbeat â done/blocked/failed with progress.
- You can retry jobs without losing history (
job_runs.attempt). - You can do clean batch processing without duplicates (
job_items). - You can support family edits without losing provenance (
scene_people/scene_tags/scene_noteswithsource/added_by/confidence/needs_review). - You can represent reality where one reel has multiple files (
reel_media_assets).
One design choice to confirm
Do you want events and locations to be single per scene (current schema), or potentially multiple?
- If multiple, weâd add
scene_eventsandscene_locationsjoin tables similar toscene_people.
If you answer that, I can give you the final âv1 lockedâ schema (with the exact join tables you need) and a Juniper prompt to implement it in NocoDB step-by-step.
multiple
Cool â if events/locations can be multiple per scene, then you definitely want join tables (same pattern as scene_people / scene_tags) so you can store provenance + confidence per link.
Change 1: update scenes to remove single event/location (or make them âprimaryâ)
Iâd recommend this (best of both worlds):
- Replace
event_id/location_idwith primary fields that are optional convenience pointers. - The real truth lives in join tables.
"scenes": {
"id": "integer_auto",
"scene_key": "text_unique",
"reel_media_asset_id": "link_reel_media_assets",
"scene_index": "integer_nullable",
"start_timecode": "decimal",
"end_timecode": "decimal",
"duration_s": "decimal_nullable",
"primary_event_id": "link_events_nullable",
"primary_location_id": "link_locations_nullable",
"status": "select(new,tagged,reviewed,needs_review,error)",
"confidence": "decimal_nullable",
"raw_model_response": "text_long_nullable",
"created_at": "datetime"
}
(If you prefer maximum simplicity, you can drop the primary fields entirely and rely only on joins.)
Change 2: add scene_events join table
"scene_events": {
"id": "integer_auto",
"scene_id": "link_scenes",
"event_id": "link_events",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
}
Change 3: add scene_locations join table
"scene_locations": {
"id": "integer_auto",
"scene_id": "link_scenes",
"location_id": "link_locations",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
}
Practical rule for agents + humans
- If an agent assigns multiple events/locations, it writes rows into
scene_events/scene_locations. - If you want a âmainâ event/location for browsing, set
primary_event_id/primary_location_idto the highest-confidence one (but itâs optional).
If you want, I can now rewrite the full final v1 schema in one JSON block (including these tables) so Juniper can implement it in NocoDB without having to splice pieces together.
yes rewrite the full final v1 schema
{
"contributors": {
"id": "integer_auto",
"display_name": "text",
"type": "select(human,agent)",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"reels": {
"id": "integer_auto",
"reel_code": "text_unique",
"title": "text_nullable",
"format": "text_nullable",
"film_stock": "text_nullable",
"date_acquired": "date_nullable",
"condition": "text_nullable",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"reel_media_assets": {
"id": "integer_auto",
"reel_id": "link_reels",
"asset_role": "select(raw_scan,stabilized,proxy,other)",
"source_type": "select(immich,filesystem,other)",
"source_locator": "text",
"immich_asset_id": "text_nullable",
"original_filename": "text_nullable",
"duration_s": "decimal_nullable",
"fps": "decimal_nullable",
"width": "integer_nullable",
"height": "integer_nullable",
"checksum_hash": "text_nullable",
"status": "select(new,processing,processed,needs_review,error)",
"ingested_at": "datetime_nullable",
"processed_at": "datetime_nullable",
"created_at": "datetime"
},
"people": {
"id": "integer_auto",
"name": "text",
"aliases": "text_long_nullable",
"birth_year": "integer_nullable",
"relationship": "text_nullable",
"immich_person_id": "text_nullable",
"immich_person_name": "text_nullable",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"events": {
"id": "integer_auto",
"name": "text",
"event_type": "text_nullable",
"start_date": "date_nullable",
"end_date": "date_nullable",
"description": "text_long_nullable",
"created_at": "datetime"
},
"locations": {
"id": "integer_auto",
"name": "text",
"location_type": "text_nullable",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"tags": {
"id": "integer_auto",
"name": "text_unique",
"category": "text_nullable",
"color": "text_nullable",
"description": "text_long_nullable",
"created_at": "datetime"
},
"scenes": {
"id": "integer_auto",
"scene_key": "text_unique",
"reel_media_asset_id": "link_reel_media_assets",
"scene_index": "integer_nullable",
"start_timecode": "decimal",
"end_timecode": "decimal",
"duration_s": "decimal_nullable",
"primary_event_id": "link_events_nullable",
"primary_location_id": "link_locations_nullable",
"status": "select(new,tagged,reviewed,needs_review,error)",
"confidence": "decimal_nullable",
"raw_model_response": "text_long_nullable",
"created_at": "datetime"
},
"scene_people": {
"id": "integer_auto",
"scene_id": "link_scenes",
"person_id": "link_people",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
},
"scene_tags": {
"id": "integer_auto",
"scene_id": "link_scenes",
"tag_id": "link_tags",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
},
"scene_events": {
"id": "integer_auto",
"scene_id": "link_scenes",
"event_id": "link_events",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
},
"scene_locations": {
"id": "integer_auto",
"scene_id": "link_scenes",
"location_id": "link_locations",
"source": "select(agent,human,import)",
"added_by_contributor_id": "link_contributors_nullable",
"confidence": "decimal_nullable",
"needs_review": "boolean",
"created_at": "datetime"
},
"scene_notes": {
"id": "integer_auto",
"scene_id": "link_scenes",
"author_contributor_id": "link_contributors_nullable",
"note": "text_long",
"created_at": "datetime"
},
"face_frames": {
"id": "integer_auto",
"scene_id": "link_scenes",
"reel_media_asset_id": "link_reel_media_assets",
"frame_timecode": "decimal",
"frame_path": "text_nullable",
"immich_asset_id": "text_nullable",
"bbox_x": "decimal_nullable",
"bbox_y": "decimal_nullable",
"bbox_w": "decimal_nullable",
"bbox_h": "decimal_nullable",
"person_id": "link_people_nullable",
"person_index": "integer_nullable",
"person_description": "text_long_nullable",
"review_status": "select(new,matched,needs_review,rejected)",
"created_at": "datetime"
},
"jobs": {
"id": "integer_auto",
"job_type": "select(import_reel,detect_scenes,tag_scenes,extract_face_frames,link_faces_to_people,qc_review,export)",
"status": "select(queued,running,blocked,done,failed,cancelled)",
"priority": "integer",
"target_type": "select(reel,reel_media_asset,scene,other)",
"target_id": "integer_nullable",
"params": "text_long_nullable",
"progress_current": "integer",
"progress_total": "integer_nullable",
"progress_unit": "text_nullable",
"created_at": "datetime",
"started_at": "datetime_nullable",
"last_heartbeat_at": "datetime_nullable",
"finished_at": "datetime_nullable",
"assigned_agent": "text_nullable",
"summary": "text_long_nullable",
"blocker": "text_long_nullable",
"error": "text_long_nullable"
},
"job_runs": {
"id": "integer_auto",
"job_id": "link_jobs",
"attempt": "integer",
"status": "select(running,blocked,done,failed)",
"runner": "text_nullable",
"started_at": "datetime",
"last_heartbeat_at": "datetime_nullable",
"finished_at": "datetime_nullable",
"log": "text_long_nullable",
"metrics": "text_long_nullable",
"error": "text_long_nullable",
"ops_run_report_id": "integer_nullable"
},
"job_items": {
"id": "integer_auto",
"job_id": "link_jobs",
"entity_type": "select(reel_media_asset,scene,face_frame,other)",
"entity_id": "integer",
"status": "select(queued,done,needs_review,error)",
"notes": "text_long_nullable",
"created_at": "datetime"
},
"artifacts": {
"id": "integer_auto",
"job_run_id": "link_job_runs",
"artifact_type": "select(html_report,csv_export,thumbnail,other)",
"uri": "text",
"notes": "text_long_nullable",
"created_at": "datetime"
}
}
OK, what changes do we need to make to the watch database schema? please give me a commprehensive prompt to give to Rosemary to update the current DB
Hereâs a comprehensive, additive (non-destructive) prompt you can give Rosemary to upgrade the Watch base schema so it supports reliable background enrichment (evidence-first, time-series valuation, documents, images) and doesnât overwrite your collector notes.
Prompt for Rosemary: upgrade Watch DB schema for evidence-first enrichment + valuation history
You are Rosemary. Task: upgrade the existing Watch Database (NocoDB base backed by Postgres) so it supports reliable, reviewable enrichment work.
NON-NEGOTIABLE RULES
- Do NOT delete existing tables/columns.
- Prefer additive changes: create new tables + add a few new columns.
- Do not overwrite human-written narrative fields (Description / Case/Dial/Bezel/Movement descriptions) unless explicitly marked as âagent_suggestedâ.
- Evidence-first: never fill a field unless you can attach at least one source (URL/file) supporting it.
- No secrets/credentials in any summary fields.
PHASE 0 â INVENTORY CURRENT STATE
1) Identify the current core table name (likely "watches") and its primary key.
2) Ensure there is a stable internal ID:
- If it doesnât exist, add `watch_id` as the primary key (or ensure `id` exists).
3) Identify the âLinksâ column (and any stray columns like âUnnamed: 43â) but do not delete anything yet.
PHASE 1 â ADD THE MISSING TABLES (create these new tables)
A) watch_sources (this is the most important addition)
- id (auto)
- watch_id (link to watches)
- source_type (single select): manufacturer | press | listing | auction | forum | receipt | manual | other
- url (text)
- retrieved_at (datetime)
- title (text nullable)
- excerpt (long text nullable) -- short quoted snippet / key facts
- fields_supported (long text or json) -- e.g. "case_size, movement, year, water_resistance"
- confidence (decimal nullable)
- archived_copy_uri (text nullable) -- optional: local PDF path / saved html location
- notes (long text nullable)
B) watch_documents (for your local file archive: receipts, PDFs, manuals)
- id
- watch_id (link)
- source_type (select): filesystem | email | drive | scan | other
- source_locator (text) -- path / message-id / url
- filename (text nullable)
- mime_type (text nullable)
- checksum_hash (text nullable)
- doc_type (select): receipt | invoice | manual | warranty | press_pdf | other
- status (select): new | processed | needs_review | error
- summary (long text nullable) -- NO sensitive info, just âwhat it isâ
- created_at (datetime)
C) watch_transactions (purchase + sale events; donât store this as single columns)
- id
- watch_id (link)
- transaction_type (select): purchase | sale | service_cost | other
- date (date/datetime nullable)
- seller (text nullable)
- seller_item_no (text nullable)
- purchased_from (text nullable)
- currency (text nullable)
- amount_original (decimal nullable)
- amount_usd_total (decimal nullable) -- your âTotal Purchase Price USD w/tax+shippingâ
- taxes_shipping_included (boolean nullable)
- notes (long text nullable)
- document_id (link watch_documents nullable)
- created_at (datetime)
D) watch_valuations (time series; many values over time)
- id
- watch_id (link)
- valuation_date (date)
- value_usd (decimal)
- valuation_type (select): market_estimate | insurance | auction_result | comp_listing | other
- source_id (link watch_sources nullable)
- notes (long text nullable)
- created_at (datetime)
E) watch_service_events (normalize service history)
- id
- watch_id (link)
- service_date (date nullable)
- serviced_by (text nullable)
- notes (long text nullable)
- document_id (link watch_documents nullable)
- created_at (datetime)
F) watch_images (link Immich assets to watches)
- id
- watch_id (link)
- immich_asset_id (text)
- role (select): hero | wrist_shot | movement | dial | caseback | paperwork | other
- notes (text nullable)
- created_at (datetime)
(OPTIONAL if you want accessories structured)
G) watch_accessories
- id
- watch_id (link)
- accessory_type (text) -- box, papers, extra strap, tool, etc.
- description (text nullable)
- document_id (link watch_documents nullable)
- created_at (datetime)
PHASE 2 â ADD A FEW COLUMNS TO THE EXISTING watches TABLE (minimal)
Add:
- needs_review (boolean)
- enrichment_status (select): new | in_progress | enriched | needs_review | blocked
- last_enriched_at (datetime nullable)
- enrichment_notes (long text nullable)
- primary_reference (text nullable) -- normalized ref number (if you want)
- primary_serial (text nullable) -- normalized serial (if you want)
PHASE 3 â LIGHT MIGRATION (do not delete old data)
1) Parse the existing "Links" column:
- For each URL, create a watch_sources row (source_type=other initially).
- Keep the original Links column unchanged.
2) Move/duplicate purchase info into watch_transactions:
- Create one purchase transaction per watch if purchase price fields exist.
- Keep original purchase columns unchanged for now.
3) If "Dates Serviced / Serviced by" exists:
- Create watch_service_events rows (best-effort parsing).
- Keep original text fields unchanged.
PHASE 4 â CREATE OPERATIONAL VIEWS (so humans can work)
Create saved views like:
- "Needs Enrichment" (enrichment_status in new/in_progress)
- "Needs Review" (needs_review=true OR enrichment_status=needs_review)
- "Missing Key Fields" (filter where important fields are empty)
- "Valuation History" (group valuations by watch)
- "Sources Missing" (watches with 0 watch_sources rows)
PHASE 5 â DEFINE THE ENRICHMENT CONTRACT (so background work is reliable)
For background runs:
- Batch size: enrich 10 watches per run.
- For each watch:
1) add 2â3 watch_sources first
2) only then fill empty fields that are supported by sources
3) if conflicting sources: mark needs_review=true and record conflict in enrichment_notes
- Update the shared ops_run_reports row each run:
- project=watch, progress_unit=watches, progress_current=# enriched, heartbeat every 30â60 min
- Never overwrite human narrative descriptions; if you want, write agent suggestions into enrichment_notes or a separate âagent_suggested_*â field.
SUCCESS CRITERIA
- The schema supports: evidence-first enrichment, local document indexing, valuation time series, and Immich-linked photos.
- We can answer: âWhat did Rosemary enrich today?â and âWhich watches have no sources yet?â
If you paste back (1) your actual current NocoDB table name for watches and (2) whether you already have any separate tables besides the spreadsheet-style one, I can tighten this prompt to match your exact setup (and suggest which fields to treat as âdo not touchâ).
Watch Database â all data copied natively â
Table Rows
Watches 135
Watch Specs 135
Spec Conflicts 449
Service History 0 (empty)
Watch Links 0 (empty)
Nice â youâre already most of the way there. The fact you have:
- Watches (135)
- Watch Specs (135) (1:1 canonical specs)
- Spec Conflicts (449) (candidate/uncertain facts)
- Service History (empty)
- Watch Links (empty)
âŚis actually an excellent foundation for an evidence-first + conflict-resolution enrichment pipeline.
What you need to change in the Watch schema
1) Turn Watch Links into your real evidence table (watch_sources)
Right now itâs empty, but it should become the âsource of truth for citationsâ that powers everything else.
Minimum columns to add to Watch Links:
watch_id(link to Watches)source_type(manufacturer / press / listing / auction / receipt / forum / other)
urlretrieved_attitle(optional)excerpt(short quoted snippet / key facts)fields_supported(text/json: which spec fields this source supports)confidence(optional)archived_copy_uri(optional)
2) Make Spec Conflicts explicitly cite a Watch Link row
Spec Conflicts becomes much more useful if each candidate fact points to a source.
Add to Spec Conflicts (if not already present):
watch_id(link)field_name(e.g., case_size, movement, water_resistance)candidate_valuesource_link_id(link to Watch Links)confidence
status(open|accepted|rejected|needs_review)notes
Then the rule is:
- Agents add candidates into Spec Conflicts (with source)
- Only accepted candidates get written into Watch Specs
3) Add missing âtime seriesâ tables (valuation + transactions)
Your current schema doesnât model history well. Add:
- Watch Transactions (purchase/sale)
- Watch Valuations (value over time)
This is how you track market value without overwriting fields.
4) Fill Service History schema (itâs empty now)
It should at least have:
watch_id,service_date,serviced_by,cost,notes,document_link(optional)
Comprehensive prompt for Rosemary (tailored to your current tables)
You are Rosemary. Task: upgrade the existing Watch Database base (NocoDB/Postgres) to support evidence-first enrichment using the existing tables: Watches, Watch Specs, Spec Conflicts, Service History, Watch Links.
NON-DESTRUCTIVE RULES
- Do not delete columns or tables.
- Prefer additive changes: add columns, add new tables, create views.
- Do not overwrite human-written narrative fields unless explicitly approved.
- Evidence-first: do not populate Watch Specs unless there is at least one Watch Links row supporting the value (or we mark it needs_review).
PHASE 0 â VERIFY RELATIONSHIPS
1) Confirm there is a stable watch primary key: Watches.id (or equivalent).
2) Ensure Watch Specs has a 1:1 link to Watches:
- If missing, add Watch Specs.watch_id (link to Watches) and populate so each watch has one specs row.
3) Ensure Spec Conflicts links to Watches:
- If missing, add Spec Conflicts.watch_id (link to Watches).
PHASE 1 â UPGRADE "WATCH LINKS" INTO A REAL EVIDENCE TABLE
Watch Links is currently empty. Modify it to include at minimum:
- id (auto)
- watch_id (link to Watches)
- source_type (single select): manufacturer | press | listing | auction | forum | receipt | manual | other
- url (text)
- retrieved_at (datetime)
- title (text nullable)
- excerpt (long text nullable)
- fields_supported (long text/json nullable)
- confidence (decimal nullable)
- archived_copy_uri (text nullable)
- notes (long text nullable)
Create a view: "Sources Missing" = watches with 0 Watch Links rows.
PHASE 2 â UPGRADE "SPEC CONFLICTS" SO EVERY CANDIDATE HAS A CITATION
Modify Spec Conflicts to include (add any missing columns):
- watch_id (link to Watches)
- field_name (text)
- candidate_value (text)
- source_link_id (link to Watch Links)
- confidence (decimal nullable)
- status (single select): open | accepted | rejected | needs_review
- notes (long text nullable)
- created_at (datetime)
Rule: any new candidate spec MUST reference source_link_id.
PHASE 3 â DEFINE CANONICAL VS CANDIDATE BEHAVIOR
- Watch Specs is canonical ("current best known truth").
- Spec Conflicts is where agents put candidates and disagreements.
- Agents should:
1) add Watch Links first
2) add candidates into Spec Conflicts (status=open)
3) only write into Watch Specs when either:
- confidence is high AND source is strong, OR
- a human has accepted the conflict (status=accepted)
PHASE 4 â ADD TIME-SERIES TABLES (NEW)
Create Watch Transactions:
- id, watch_id(link), transaction_type(purchase|sale|other), date, seller, purchased_from, currency, amount_original, amount_usd_total, fees_notes, created_at
Create Watch Valuations:
- id, watch_id(link), valuation_date, value_usd, valuation_type(market_estimate|insurance|auction_result|comp_listing|other), source_link_id(link Watch Links nullable), notes, created_at
PHASE 5 â FINISH SERVICE HISTORY TABLE (currently empty)
Modify Service History to have:
- id, watch_id(link), service_date, serviced_by, cost, notes, source_link_id(link Watch Links nullable), created_at
PHASE 6 â ADD A FEW STATUS COLUMNS TO WATCHES (OPTIONAL BUT RECOMMENDED)
On Watches add:
- enrichment_status (new|in_progress|enriched|needs_review|blocked)
- needs_review (boolean)
- last_enriched_at (datetime)
- enrichment_notes (long text)
PHASE 7 â CREATE OPERATIONAL VIEWS
- Needs Enrichment (enrichment_status in new/in_progress)
- Needs Review (needs_review=true OR conflicts in status=needs_review)
- Open Conflicts (Spec Conflicts.status=open)
- Accepted Conflicts Ready to Apply (status=accepted)
- Sources Missing (0 Watch Links)
SUCCESS CRITERIA
- Watch Links is populated and used as evidence.
- Spec Conflicts candidates always cite a Watch Links row.
- Watch Specs stays clean and trustworthy.
- We can track valuation over time without overwriting fields.
If you paste the current column list for Spec Conflicts (just field names), I can tighten the âadd missing columnsâ part so Rosemary isnât guessing whatâs already there.
Watch ID
Field
- Existing Value
New Value
Source
- Detected At
- Resolved At
⢠Resolution
Keep Value
Perfect â your Spec Conflicts table is already basically the right shape. You donât need to reinvent it; you just need to tighten one thing:
The main change: make Source a real FK into âWatch Linksâ (not free text)
Right now Source is presumably a text field. If you want evidence-first enrichment, you want every conflict to point to a structured source row.
What you already have (mapped)
Watch IDâ =watch_idFieldâ =field_nameExisting ValueâNew Valueâ = candidate_valueDetected AtâResolved AtâResolutionâ (this is your status)Keep Valueâ (this can drive the âapply to Watch Specsâ step)
Minimal schema upgrades to Spec Conflicts
Add (or convert) the following:
- Convert
Sourceto a Link field toWatch Links
- Best: change the
Sourcecolumn type â Link to Watch Links - Safer (if you donât want to risk type conversion): add a new column
Source Link= link to Watch Links- keep old
Sourcetext as legacy
- Add
Confidence(optional but very useful)
confidence(decimal 0â1 or 0â100)
- Add
Notes(optional)
notes(long text) â e.g. why it conflicted, what to verify
Thatâs it. Everything else you wanted (status + resolution workflow) is already there.
Tight definitions (so agents behave consistently)
Standardize Resolution values to something like:
open(new conflict, not reviewed)
accepted(New Value is correct â apply it to Watch Specs)rejected(Existing Value stays)needs_review(canât decide / conflicting sources)
Then:
Resolved Atis only set when resolution != openKeep Valueshould match the resolution decision (or you can stop using it if Resolution is enough)
Updated âcomprehensive Rosemary promptâ (tightened to your actual columns)
You are Rosemary. Task: upgrade the existing Watch Database base to support evidence-first enrichment using the existing tables:
Watches, Watch Specs, Spec Conflicts, Service History (empty), Watch Links (empty).
NON-DESTRUCTIVE RULES
- Do not delete tables/columns.
- Prefer additive changes.
- Evidence-first: donât write into Watch Specs unless you can cite a Watch Links row.
PHASE 1 â IMPLEMENT WATCH LINKS (it is currently empty)
Create/modify Watch Links to include:
- id (auto)
- watch_id (link to Watches)
- source_type (manufacturer|press|listing|auction|forum|receipt|manual|other)
- url (text)
- retrieved_at (datetime)
- title (text nullable)
- excerpt (long text nullable)
- fields_supported (long text/json nullable)
- confidence (decimal nullable)
- archived_copy_uri (text nullable)
- notes (long text nullable)
PHASE 2 â TIGHTEN SPEC CONFLICTS (you already have the right columns)
Your Spec Conflicts columns are:
Watch ID, Field, Existing Value, New Value, Source, Detected At, Resolved At, Resolution, Keep Value.
Do the minimal upgrades:
- Convert Spec Conflicts.Source into a LINK to Watch Links (preferred), OR add a new link column Source Link â Watch Links and keep Source as legacy text.
- Add Confidence (decimal nullable)
- Add Notes (long text nullable)
Standardize Spec Conflicts.Resolution to: open | accepted | rejected | needs_review
PHASE 3 â OPTIONAL: ADD HISTORY TABLES (recommended)
Create:
- Watch Transactions (purchase/sale events)
- Watch Valuations (time series values)
and link them to Watches (and optionally to Watch Links for citations).
PHASE 4 â BACKFILL (first useful work)
- For 10 watches:
1) create 2â3 Watch Links per watch (real URLs)
2) propose specs as Spec Conflicts rows with Source Link set
3) only set Resolution=accepted when evidence is strong; then update Watch Specs accordingly.
SUCCESS CRITERIA
- Every Spec Conflict has a Source Link pointing to Watch Links.
- Watch Specs changes are traceable to sources.
- âOpen conflictsâ and âMissing sourcesâ views work.
If you tell me whether Source is currently text or already a link-type field, I can tell you the safer path (convert in place vs add a new âSource Linkâ column and migrate gradually).
its currently text and pooints to the json file from the nights serches brave_search_2026-03-02
Thatâs actually a good âaudit trailâ â but itâs the wrong granularity for evidence. A JSON file path (âbrave_search_2026-03-02âŚâ) tells you where the batch came from, not which URL supports this specific field change.
What to do (donât convert in place)
Since Source is currently a text pointer and you already have 449 rows, the safest approach is:
- Keep
Sourceexactly as-is (treat it aslegacy_source_ref) - Add a new structured citation path going forward:
- a link to a Watch Links row (the specific URL)
- optionally the exact URL as text for convenience/migration
Minimal Spec Conflicts additions
Add columns to Spec Conflicts:
source_url(text, nullable) â the actual URL that supports the New Valuesource_link_id(Link â Watch Links, nullable) â the normalized citation- (optional)
confidence(decimal nullable) - (optional)
notes(long text nullable)
Then going forward:
- always set
source_link_id(or at leastsource_url) - keep
Sourceas the raw âbatch file referenceâ only
Watch Links additions
In Watch Links, make sure you have:
url(text)retrieved_atsource_type- and add:
archived_copy_uri(text nullable) = your JSON file path likebrave_search_2026-03-02...search_run_ref(text nullable) if you want to store the filename separately
This gives you both:
- precise citation (URL row)
- traceability back to the raw search dump (JSON file)
Prompt you can give Rosemary (targeted to your current situation)
You are Rosemary. We discovered Spec Conflicts.Source is currently TEXT and points to nightly Brave search JSON files (e.g., brave_search_2026-03-02). We want to keep that legacy pointer, but add proper per-field citations.
TASK
1) Do NOT modify or delete Spec Conflicts.Source. Treat it as legacy_source_ref.
2) Update Spec Conflicts schema (add columns):
- source_url (text, nullable)
- source_link_id (LINK to Watch Links, nullable)
- confidence (decimal nullable)
- notes (long text nullable)
3) Update Watch Links schema (if missing):
- url (text)
- retrieved_at (datetime)
- source_type (select manufacturer|press|listing|auction|forum|receipt|manual|other)
- excerpt (long text nullable)
- fields_supported (long text/json nullable)
- confidence (decimal nullable)
- archived_copy_uri (text nullable) <-- store the brave_search_YYYY-MM-DD json path here when relevant
4) New rule for all future enrichment:
- Every new Spec Conflicts row must include either:
a) source_link_id populated (preferred), OR
b) source_url populated (fallback)
- If the only thing we have is a JSON dump filename, store it in Spec Conflicts.Source (legacy) AND also store it in Watch Links.archived_copy_uri when creating the actual URL row.
5) (Optional, best-effort migration)
- For the most important/open conflicts, backfill source_url/source_link_id:
- open the referenced brave_search JSON file
- pick the most relevant URL result
- create a Watch Links row for that URL (archived_copy_uri = that JSON file)
- set Spec Conflicts.source_link_id to that Watch Links row
SUCCESS
- Existing conflicts remain intact.
- New conflicts are properly citable to specific URLs via Watch Links.
- We can filter âConflicts missing source_link_idâ as a cleanup queue.
If you tell me whether your Brave search JSON files include the original query string and a list of result URLs/snippets (they usually do), I can suggest an even cleaner optional table (search_runs / search_results) â but the approach above is the minimal change that fixes your reliability problem immediately.
Yes! Both scripts do include the original query string and result URLs/snippets:
*nightly_watch_research_job.py * â /tmp/watch_research_results.json
{
"search_id": "...",
"query": "query_string_here", // â Original query
"results": [...], // â Brave API results with URLs/snippets
"status": "success",
"count": 5,
"pt": "brand/model",
"field": "field_name",
"timestamp": "..."
}
lean_watch_research.py â /projects/watch-vault/data/research_queue.json
{
"aq": "watch_id",
"query": "query_string_here", // â Original query
"results": [...], // â Brave API results
"manufacturer": "brand",
"model": "model_name",
"queued_at": "..."
}
Both have the query + full Brave web results, so they're ready to be normalized into a cleaner search_runs (metadata)
/ search_results (individual result rows) schema if that would help reliability/queryability. What structure would you
recommend?
Yes â given you already have query + full results[], itâs absolutely worth normalizing. The clean pattern is a 3-layer pipeline:
- Raw search runs (what query ran, for which watch/field, when, did it succeed)
- Raw search results (each URL/snippet/rank returned)
- Curated evidence (a small subset promoted into your existing âWatch Linksâ / citations workflow)
That separation prevents âLinksâ from turning into a junk drawer and makes it easy to re-run, dedupe, and audit.
Recommended tables + relationships
1) search_runs (one row per query execution)
This corresponds to each JSON object your scripts emit.
search_runs
id(auto)
provider(select: brave)script_name(text) ânightly_watch_research_job.py/lean_watch_research.pyexternal_search_id(text nullable) â yoursearch_idwatch_id(link to Watches; nullable if some searches arenât watch-specific)field_name(text nullable) â yourfieldpt(text nullable) â yourpt(âbrand/modelâ, etc.)manufacturer(text nullable)model(text nullable)query(text)status(select: success | error)result_count(integer)executed_at(datetime) â yourtimestamp/queued_atraw_json_uri(text) â file path like/tmp/watch_research_results.jsoncreated_at(datetime)
Why: lets you ask âwhat did we search last night for Watch 37 field=water_resistance?â and detect repeated queries.
2) search_results (one row per URL/snippet returned)
This is the results[] array exploded into rows.
search_results
id(auto)search_run_id(link to search_runs)rank(integer) â 1..Nurl(text)domain(text nullable)title(text nullable)snippet(long text nullable)provider_score(decimal nullable, if available)created_at(datetime)
Recommended uniqueness/dedupe:
- Unique key on
(search_run_id, url)(or(search_run_id, rank))
Why: you can query across results, dedupe URLs, and promote the best result without re-parsing JSON files.
3) Keep your existing Watch Links as the curated âevidenceâ layer
This stays your âcitation tableâ, but now it can point back to the raw search result that produced it.
Add to Watch Links:
search_result_id(link to search_results, nullable)search_run_id(link to search_runs, nullable) â optional conveniencearchived_copy_uri(text nullable) â raw JSON filename OR saved page
Flow: promote a search_results row â create a Watch Links row linked to it â then Spec Conflicts points to Watch Links.
This is the key chain:
Spec Conflicts â Watch Links (curated) â Search Results (raw) â Search Runs (query metadata)
Optional (but very helpful): research_queue as a real table
Since you already have research_queue.json, the DB equivalent is:
research_queue
idwatch_idfield_namequerystatus(queued|running|done|error)priorityqueued_atlast_attempt_atattempts
notes
This reduces duplicates and prevents bursty âflood the request queueâ behavior because you can throttle by selecting only N queued rows per run.
Quick implementation choice (keeps migration easy)
Because you already have conflicts pointing at a JSON filename, Iâd do this:
- Keep
Spec Conflicts.Sourceas legacy text (raw file pointer) - Add
Spec Conflicts.source_link_id(link â Watch Links) - Populate
search_runs/search_resultsgoing forward - Gradually backfill old conflicts by promoting the best URL into Watch Links
If you want, I can write a single comprehensive âRosemary promptâ to implement search_runs + search_results + Watch Links backreferences, plus a lightweight backfill plan from your two JSON files. To make it exact, tell me:
- Do you want
search_runs/search_resultsinside the Watch Database base, or in the shared Ops base? - Roughly how many queries per night (order of magnitude: 100s vs 10,000s)? This affects whether we store full snippets or keep only top N.
1 watch database, 2. 100-300
Got it. Since you want this in the Watch Database base, and youâre only doing ~100â300 queries/night, you can store full snippets/results comfortably. Hereâs a comprehensive Rosemary prompt that (a) adds search_runs + search_results, (b) links them into your existing Watch Links + Spec Conflicts workflow, and (c) includes a clean backfill/import plan from your two JSON outputs.
Comprehensive prompt for Rosemary: normalize Brave search JSON into search_runs/search_results (Watch Database base)
You are Rosemary. Task: In the Watch Database NocoDB base (backed by Postgres), implement a normalized search logging schema for Brave results and wire it into the existing workflow:
Watches, Watch Specs, Spec Conflicts, Service History, Watch Links.
CONSTRAINTS
- Non-destructive: do not delete existing tables/columns.
- Additive changes only.
- Keep Spec Conflicts.Source as legacy text (it currently points to brave_search_YYYY-MM-DD json files).
- Going forward, every conflict should be citable to a specific URL via Watch Links.
INPUT FILE FORMATS
1) nightly_watch_research_job.py â /tmp/watch_research_results.json
Each record has: search_id, query, results[], status, count, pt, field, timestamp
2) lean_watch_research.py â /projects/watch-vault/data/research_queue.json
Each record has: aq (watch_id), query, results[], manufacturer, model, queued_at
GOAL
Normalize both into:
- search_runs (one row per query execution)
- search_results (one row per URL/snippet returned)
Then make Watch Links able to reference a specific search result, and make Spec Conflicts able to reference a Watch Links row.
PHASE 1 â CREATE TABLE: search_runs
Create a new table named: search_runs
Columns:
- id (auto)
- provider (single select; default "brave")
- script_name (text) -- nightly_watch_research_job.py | lean_watch_research.py
- external_search_id (text nullable) -- search_id from nightly script
- watch_id (link to Watches, nullable) -- from aq when available
- field_name (text nullable) -- from field (nightly script)
- pt (text nullable) -- from pt (nightly script)
- manufacturer (text nullable) -- from lean script if present
- model (text nullable) -- from lean script if present
- query (text)
- status (single select): success | error
- result_count (integer)
- executed_at (datetime) -- timestamp or queued_at
- raw_json_uri (text) -- file path the record came from
- created_at (datetime)
PHASE 2 â CREATE TABLE: search_results
Create a new table named: search_results
Columns:
- id (auto)
- search_run_id (link to search_runs)
- rank (integer) -- 1..N in returned order
- url (text)
- domain (text nullable)
- title (text nullable)
- snippet (long text nullable)
- created_at (datetime)
(We only have 100â300 searches/night and typically 5 results each, so storing snippets is fine.)
PHASE 3 â UPDATE EXISTING TABLE: Watch Links (currently empty)
Modify Watch Links to support being a curated evidence layer sourced from search_results:
Add columns if missing:
- watch_id (link to Watches)
- source_type (select): manufacturer | press | listing | auction | forum | receipt | manual | other
- url (text)
- retrieved_at (datetime)
- title (text nullable)
- excerpt (long text nullable)
- fields_supported (long text/json nullable)
- confidence (decimal nullable)
- archived_copy_uri (text nullable) -- can store the json filename or saved page path
- search_run_id (link to search_runs, nullable)
- search_result_id (link to search_results, nullable)
- notes (long text nullable)
PHASE 4 â UPDATE EXISTING TABLE: Spec Conflicts (449 rows)
Current columns: Watch ID, Field, Existing Value, New Value, Source, Detected At, Resolved At, Resolution, Keep Value
Do NOT remove/rename existing columns. Add:
- source_url (text nullable) -- actual URL if known
- source_link_id (LINK to Watch Links, nullable) -- curated citation
- confidence (decimal nullable)
- notes (long text nullable)
We will keep Spec Conflicts.Source as legacy raw json file reference.
Standardize Spec Conflicts.Resolution to values:
- open | accepted | rejected | needs_review
(If existing values differ, map them; do not destroy history.)
PHASE 5 â IMPORT / NORMALIZE THE JSON FILES INTO TABLES
Implement a reliable import method (choose the simplest workable one: NocoDB import or a small script that inserts into Postgres).
Import logic:
A) For each record in each JSON file:
- Create a search_runs row with:
- script_name based on file
- provider=brave
- external_search_id if present
- watch_id if present (aq)
- field_name if present
- pt if present
- manufacturer/model if present
- query, status, result_count, executed_at, raw_json_uri
- Then create search_results rows for each result in results[]:
- search_run_id = the created run
- rank = order in list
- url/title/snippet/domain
B) Dedupe guidance (best effort):
- If importing repeatedly, avoid duplicating runs:
- primary key strategy: (external_search_id) when available
- otherwise: (script_name + query + executed_at) as a uniqueness heuristic
PHASE 6 â PROMOTION WORKFLOW (raw results â curated evidence â conflicts)
Define a standard promotion rule for enrichment work:
1) When you want to cite a URL for a watch field:
- pick a search_results row
- create a Watch Links row referencing:
- watch_id
- url/title/snippet (as excerpt)
- search_run_id + search_result_id
- archived_copy_uri = raw_json_uri from that run
- then set Spec Conflicts.source_link_id to that Watch Links row
- optionally set Spec Conflicts.source_url to the same URL for quick readability
2) Going forward, any new Spec Conflicts row must set source_link_id (preferred) or at minimum source_url.
PHASE 7 â CREATE OPERATIONAL VIEWS (so this is usable)
Create saved views:
- Search Runs (Today)
- Search Runs (By Watch)
- Search Results (Top Ranked)
- Watch Links Missing (Watches with 0 links)
- Spec Conflicts Missing Citation (Resolution=open AND source_link_id is empty)
- Spec Conflicts Open (Resolution=open)
SUCCESS CRITERIA
- search_runs/search_results populate from both JSON sources.
- Watch Links can be created from search_results with backreferences.
- Spec Conflicts can cite Watch Links via source_link_id while keeping legacy Source text unchanged.
- We can answer: âWhat searches ran last night for Watch X?â and âWhich open conflicts have no citation yet?â
If you want one extra (optional) reliability upgrade: add a boolean on search_runs like normalized=true and have the importer set it, so you never re-import the same run twice.
If youâd like, I can also write the execution prompt Rosemary uses nightly: âimport last nightâs JSON â create runs/results â promote top 1â2 results per watch field into Watch Links â open conflicts as needed,â with strict batch limits to avoid request bursts.
yes do these two things,
Got it â the two things are:
- add a ânormalized/ingestedâ marker so you donât re-import the same searches forever
- a nightly execution prompt for Rosemary that does: import JSON â populate search_runs/results â promote a small number into Watch Links â open Spec Conflicts (with strict batching + reporting)
Here you go.
1) Prompt to Rosemary: add normalization fields to search_runs (dedupe + idempotency)
You are Rosemary. Update the Watch Database schema to make search import idempotent.
In the Watch Database base, modify search_runs by adding these columns:
- run_fingerprint (text) -- used to dedupe when external_search_id is missing
- normalized_status (single select): pending | done | error
- normalized_at (datetime nullable)
- normalized_error (long text nullable)
Rules:
- When importing a record, compute run_fingerprint as a stable string like:
provider + "|" + script_name + "|" + (external_search_id OR "") + "|" + (watch_id OR "") + "|" + (field_name OR "") + "|" + query + "|" + executed_at
- Before inserting a new search_runs row, check if a row already exists with:
- same external_search_id (if present), OR
- same run_fingerprint
If it exists, skip creating a duplicate run (but you may still verify results exist).
- After successfully inserting the run + all its search_results, set:
normalized_status=done, normalized_at=now
- On failure, set normalized_status=error and write normalized_error.
(If you want an even simpler version: skip run_fingerprint and dedupe only by external_search_id + (script_name, query, executed_at), but run_fingerprint is more robust.)
2) Nightly execution prompt for Rosemary (import + promote + conflicts + run report)
You are Rosemary running the nightly Watch normalization + enrichment pipeline.
Your job is NOT to do new searches tonight; your job is to normalize existing JSON outputs and convert them into structured, reviewable DB rows.
INPUTS
- /tmp/watch_research_results.json (nightly_watch_research_job.py output)
- /projects/watch-vault/data/research_queue.json (lean_watch_research.py output)
OUTPUT TABLES (must be updated)
- search_runs
- search_results
- Watch Links (curated evidence)
- Spec Conflicts (open conflicts with citations)
- (optional) Watches.enrichment_status / last_enriched_at if those exist
GLOBAL RULES
- Non-destructive: donât delete anything.
- Evidence-first: any new Spec Conflict must cite a specific URL via Watch Links (source_link_id), not just a JSON filename.
- Limit âpromotionâ work to a small batch to avoid runaway behavior.
- Heartbeat/reporting: update the shared ops_run_reports row every 30â60 minutes.
STEP 0 â START RUN REPORT
- Create a new ops_run_reports row:
project=watch, agent=Rosemary, status=running
progress_unit=watches (or queries), progress_current=0
- Heartbeat rule: update last_heartbeat_at + progress every 30â60 minutes.
STEP 1 â LOAD + VALIDATE INPUT FILES
- Load both JSON files.
- If either file is missing/empty, log a blocker in ops_run_reports and exit gracefully.
STEP 2 â NORMALIZE INTO search_runs/search_results (IDEMPOTENT)
For each record in each file:
- Compute run_fingerprint (per schema rule).
- Upsert search_runs:
- if external_search_id exists, dedupe on it
- else dedupe on run_fingerprint
- For each result in results[]:
- insert into search_results if it doesnât already exist for that search_run_id + url (or search_run_id + rank).
- Set search_runs.normalized_status=done + normalized_at.
Track counts:
- runs_imported_new
- runs_skipped_existing
- results_inserted
STEP 3 â PROMOTE A SMALL SET INTO CURATED Watch Links (BATCHED)
Goal: convert raw search_results into a usable evidence layer.
Batch limits:
- Max 10 watches promoted per nightly run
- Max 2 promoted links per watch per run
- Prefer fields that are currently empty in Watch Specs or have open conflicts
Promotion logic for a watch/field:
- Choose the best 1â2 search_results by heuristics:
- Prefer manufacturer/official sources for specs when available
- Prefer reputable press/auction for history/value context
- Avoid obvious low-quality domains
- Create a Watch Links row:
- watch_id
- url/title/snippetâexcerpt
- source_type (best guess)
- retrieved_at = search_runs.executed_at
- archived_copy_uri = search_runs.raw_json_uri
- search_run_id + search_result_id backreferences
STEP 4 â OPEN / UPDATE Spec Conflicts (WITH CITATIONS)
For each promoted Watch Link:
- If it supports a specific spec field:
- If Watch Specs field is empty:
- create Spec Conflicts row:
Watch ID, Field, Existing Value = empty, New Value = proposed, Resolution=open
Source Link = Watch Links row (source_link_id)
Detected At = now
- Else if proposed value differs from existing:
- create/update Spec Conflicts row similarly (Resolution=open)
- Keep Spec Conflicts.Source (legacy) as the JSON filename/path only; do not rely on it for citation.
IMPORTANT:
- Do NOT auto-accept/resolution unless you are extremely confident AND the field is âsafeâ.
- Default behavior is to create/open conflicts for later review.
STEP 5 â UPDATE WATCH-LEVEL STATUS (OPTIONAL)
If Watches has enrichment_status/last_enriched_at:
- Mark watches touched tonight as enrichment_status=in_progress (or enriched if you only filled missing + no conflicts)
- Update last_enriched_at
STEP 6 â FINISH RUN REPORT
Update ops_run_reports to status=done with:
- summary: runs imported, results inserted, watch links created, conflicts created/updated, watches touched
- artifacts: link to NocoDB views (âSearch Runs Todayâ, âOpen Conflictsâ, âLinks Created Todayâ)
If you hit an error that blocks progress:
- status=blocked + blocker text + next_step
Never silently stop.
If you want, I can also give you the exact views to create in NocoDB for this nightly workflow (the 4 views that make it feel like a real system: âRuns Todayâ, âLinks Created Todayâ, âOpen Conflicts Missing Citationâ, âWatches With 0 Sourcesâ).
yes tell me the views to create
Here are the high-signal NocoDB views Iâd create for the Watch Database so you can run the nightly pipeline and immediately see: âwhat ran, what was promoted, whatâs blocked, what needs review.â
Iâm assuming youâll have (or add) these tables/fields:
search_runs(withexecuted_at,normalized_status,watch_id,field_name,query)search_results(withsearch_run_id,rank,url)Watch Links(withwatch_id,url,retrieved_at/created_at,search_run_id,search_result_id)Spec Conflicts(withResolution,Detected At,Resolved At, andsource_link_idlink â Watch Links)
If any of those are missing, create the view anyway and just leave the filter piece out until the column exists.
A) Views in search_runs
1) Search Runs â Today
Filter
executed_atis today
Sort
executed_atdesc
Show- watch_id, field_name, query, status, result_count, normalized_status, raw_json_uri
2) Search Runs â Not Normalized / Failed
Filter (either/or)
normalized_status=pendingORnormalized_status=error
Sortexecuted_atdesc
Purpose- this is your âimport is broken / stuckâ alarm bell
3) Search Runs â By Watch (last 30 days)
Filter
executed_atwithin last 30 days
Sortwatch_idasc thenexecuted_atdesc
Purpose
- quick audit: âwhat searches did we run for this watch?â
B) Views in search_results
4) Search Results â Top 3 per Run
Filter
rank<= 3
Sortsearch_run_idasc thenrankasc
Purpose- makes promotion easy (you mostly care about top results)
(If you add a backref/rollup from Watch Links â Search Results, you can also make a âNot Promoted Yetâ view, but itâs optional.)
C) Views in Watch Links (your curated evidence layer)
5) Watch Links â Created Today
Filter
retrieved_atis today (orcreated_atif thatâs what you have)
Sortretrieved_at/created_atdesc
Purpose- âwhat evidence did Rosemary actually add today?â
6) Watch Links â Missing Field Support
Filter
fields_supportedis empty OR null
Purpose- cleanup queue so links arenât just random URLs
7) Watches â Missing Sources
This is technically a Watches view, but itâs sourced from Watch Links.
Requires
- a relation from Watches â Watch Links (watch_id link)
- a rollup field on Watches like
links_count
Filter
links_count= 0
Sort- whatever you want (purchase date, priority, etc.)
D) Views in Spec Conflicts (the human review queue)
8) Spec Conflicts â Open
Filter
Resolution=open
SortDetected Atdesc
Purpose
- main âthings to reviewâ queue
9) Spec Conflicts â Open (Missing Citation)
Filter
Resolution=open- AND
source_link_idis empty/null
Sort Detected Atdesc
Purpose- catches exactly the failure mode you had (conflict exists but no URL attached)
10) Spec Conflicts â Accepted (Ready to Apply)
Filter
Resolution=accepted- AND
Resolved Atis not empty
Sort
Resolved Atdesc
Purpose- this is the queue for writing accepted values into
Watch Specs(if youâre not auto-applying)
Two rollups that make the whole thing feel âdashboardyâ
If your relationships are link-fields (not plain integers), add these on Watches:
links_count= rollup count of Watch Linksopen_conflicts_count= rollup count of Spec Conflicts where Resolution=open
(If NocoDB canât filter inside rollups, just count total conflicts and use a view for open ones.)
Then you can make a killer Watches view:
11) Watches â Priority Review
Filter
open_conflicts_count> 0 ORlinks_count= 0
Sortopen_conflicts_countdesc thenlinks_countasc
If you tell me whether your Watch ID field in Spec Conflicts is a link to Watches or just an integer, I can tell you the one small tweak that makes rollups + cross-table dashboards work reliably.
currently it is a ID code in text, PT-097, but I realize that we really need a UID for every watch
Yep â you want two identifiers:
- a true UID / primary key (stable, never changes, used for all links/joins)
- your human-friendly watch code like
PT-097(unique, displayed, used in filenames/queries)
What Iâd do in NocoDB/Postgres
On Watches
- Ensure there is an
idinteger_auto primary key (or add awatch_uidUUID if you prefer UUIDs) - Keep
PT-097as a separate column:watch_code(text, unique) =PT-097
So:
- UID =
Watches.id(orwatch_uid) - Human code =
Watches.watch_code
How to migrate without breaking anything
Because Spec Conflicts.Watch ID is currently text:
- Do not delete it yet. Rename it mentally to
watch_code_legacy. - Add a new column in Spec Conflicts:
watch= Link to Watches (this becomes the real FK)
- Backfill
Spec Conflicts.watchby matching:Spec Conflicts.Watch ID(PT-097) âWatches.watch_code
- Once everything is linked and rollups work, you can keep the legacy text field forever (harmless) or hide it from views.
Do the same pattern for:
- Watch Specs (add
watchlink if it isnât already) - Watch Links (add
watchlink) - Service History (add
watchlink) - search_runs (add
watchlink; keeppt/code as text)
Optional but nice (since you have multiple numbering systems)
If you have AQ#, McGrath #, etc., consider a tiny table:
watch_identifiers
watch(link)id_type(AQ, McGrath, PRT, etc.)id_value(text)- unique on (watch, id_type)
This prevents those extra IDs from becoming random columns.
If you want, I can write a single âRosemary promptâ that does the exact NocoDB steps: add Watches.watch_code, add link columns, backfill links by matching PT codes, then create rollups/views.
yes please write that prompt
You are Rosemary. Task: migrate the Watch Database in NocoDB from using a human âWatch ID codeâ (e.g. PT-097) as the identifier to using a real UID (Watches.id) as the primary key for links/joins â WITHOUT breaking existing data.
NON-DESTRUCTIVE RULES
- Do not delete any existing columns.
- Do not overwrite existing ID-code fields; we will keep them as human-facing codes.
- Prefer additive changes: add new columns + backfill.
- Before making changes, export a snapshot of the Watch Database base (or otherwise ensure we can roll back).
PHASE 0 â INSPECT CURRENT TABLES
Confirm these tables exist:
- Watches (135)
- Watch Specs (135)
- Spec Conflicts (449)
- Service History (empty)
- Watch Links (empty)
Identify the column in Watches that currently stores the PT-style code (example values: PT-097).
- If itâs called something like âWatch IDâ, âPRT Watch Listâ, âAQ#â, etc., note the exact name.
- Verify whether Watches already has an auto primary key (usually âidâ integer auto). If not, add it.
PHASE 1 â ADD A HUMAN-FACING CODE FIELD ON Watches
1) Add a new column to Watches:
- watch_code (text, UNIQUE)
2) Populate Watches.watch_code from the existing PT-code column.
- Trim whitespace.
- Normalize casing (e.g. âPT-097â).
- If duplicates exist, do NOT guess; flag them for manual review (add a temporary âneeds_reviewâ boolean if needed).
3) Add/ensure a âdisplay nameâ formula or view uses watch_code + manufacturer/model (optional).
PHASE 2 â ADD REAL FK LINKS (Link-to-record fields) IN DEPENDENT TABLES
For each of these tables, add a new column named `watch` (Link to Watches):
- Watch Specs: add watch (link to Watches)
- Spec Conflicts: add watch (link to Watches)
- Watch Links: add watch (link to Watches) [even if empty today]
- Service History: add watch (link to Watches)
IMPORTANT: Do NOT remove existing text âWatch IDâ fields yet. They become legacy/human-readable.
PHASE 3 â BACKFILL LINKS BY MATCHING watch_code
Backfill the new link fields using the PT-code:
A) Watch Specs
- If Watch Specs has a PT-code column, match it to Watches.watch_code and set Watch Specs.watch.
- If it does NOT have a PT-code column and itâs truly 1:1 row order from import, still avoid guessing; add a temporary mapping view and do the safest match available (usually there is some shared code column).
B) Spec Conflicts
- Spec Conflicts currently has âWatch IDâ as text (PT-097).
- For each Spec Conflicts row:
- find Watches row where Watches.watch_code == Spec Conflicts.Watch ID
- set Spec Conflicts.watch link to that Watches row
- If no match: leave Spec Conflicts.watch empty and set Resolution=needs_review (or add a notes field) indicating âwatch_code not foundâ.
C) (Optional) If you create search_runs/search_results later
- Ensure search_runs.watch is a link to Watches and backfill from PT code when available.
PHASE 4 â MAKE DASHBOARD ROLLUPS WORK (add rollup fields on Watches)
Once the link fields exist, add these rollups on Watches:
1) links_count
- Rollup: count of Watch Links linked to this watch
2) open_conflicts_count
- Rollup: count of Spec Conflicts linked to this watch
- If NocoDB canât filter rollups by Resolution=open, just roll up âtotal_conflicts_countâ and rely on a Spec Conflicts view for open ones.
(Optionally also: accepted_conflicts_count, needs_review_conflicts_count)
PHASE 5 â CREATE THE HIGH-SIGNAL VIEWS (so this becomes usable immediately)
Create these views:
On Watches:
- âWatches â Missing Sourcesâ
Filter: links_count = 0
- âWatches â Has Conflictsâ
Filter: (open_conflicts_count > 0) OR (total_conflicts_count > 0)
On Spec Conflicts:
- âConflicts â Openâ
Filter: Resolution = open
Sort: Detected At desc
- âConflicts â Open Missing Watch Linkâ
Filter: Resolution = open AND watch is empty
- âConflicts â Open Missing Citationâ
(After we add source_link_id later) Filter: Resolution=open AND source_link_id is empty
PHASE 6 â VALIDATION CHECKS
- Confirm Watches.watch_code is filled for all 135 watches.
- Confirm Watch Specs.watch is filled for all 135 rows (or explain why not).
- Confirm Spec Conflicts.watch is filled for the vast majority of rows; list any unmatched codes.
- Confirm rollups compute and views populate correctly.
DELIVERABLE
Post a short summary:
- # watches with watch_code
- # Watch Specs linked to Watches
- # Spec Conflicts linked to Watches
- Any duplicates/unmatched watch codes needing manual resolution
- Links to the key NocoDB views created
If you tell me the exact column name in Watches that currently contains PT-097 (and whether Watch Specs also has that code column), I can tighten the prompt further so Rosemary doesnât have to âdiscoverâ it mid-flight.
Text: Id1
Perfect â that makes this much easier.
If the PT-style code is currently stored in Watches.Id1 (text), then in the prompt you give Rosemary you can be explicit:
Updated âRosemary promptâ (with your real column name)
You are Rosemary. We are migrating the Watch Database to use a real UID (Watches.id) for linking, while keeping the human watch code (PT-097) as a unique display field.
KNOWN FACT
- The human watch code currently lives in Watches.Id1 (type: text). Example: "PT-097".
RULES
- Do not delete existing columns.
- Keep Watches.Id1 as legacy/human-readable (we can optionally rename it later, but not required).
- Add new link fields and backfill them.
STEP 1 â Ensure Watches has a real primary key
- Confirm Watches has an auto primary key column (usually `id` integer auto). If missing, add it.
STEP 2 â Add watch_code and populate from Id1
- Add a new column on Watches: watch_code (text, UNIQUE).
- Populate it as: watch_code = TRIM(Watches.Id1).
- If any duplicates or blanks exist, stop and flag for manual cleanup (do not guess).
STEP 3 â Add link-to-record columns called `watch` in dependent tables
Add a new column named `watch` (Link â Watches) in:
- Watch Specs
- Spec Conflicts
- Watch Links
- Service History
STEP 4 â Backfill those `watch` links by matching codes
A) Spec Conflicts
- Spec Conflicts has a text Watch ID code column (the one containing PT-097).
- For each Spec Conflict row:
- Find Watches row where Watches.watch_code == Spec Conflicts.<WatchIDTextField>
- Set Spec Conflicts.watch = that Watches row
- Leave rows unmatched with watch empty and set Resolution=needs_review (or add a note).
B) Watch Specs
- If Watch Specs has the PT code in some text field, match it to Watches.watch_code and set Watch Specs.watch.
- If Watch Specs is already 1:1 with Watches by import order but has no code column, do NOT guess; identify the correct shared key first (prefer code match).
STEP 5 â Add rollups on Watches (once links exist)
- links_count = count of Watch Links rows linked to this watch
- conflicts_count = count of Spec Conflicts rows linked to this watch
STEP 6 â Create the key views
On Watches:
- Watches â Missing Sources: links_count = 0
- Watches â Has Conflicts: conflicts_count > 0
On Spec Conflicts:
- Conflicts â Open: Resolution=open
- Conflicts â Open Missing Watch Link: Resolution=open AND watch is empty
DELIVERABLE
Report:
- # watches with watch_code populated from Id1
- # Spec Conflicts successfully linked to Watches
- Any duplicates/unmatched codes found
- Links/screenshots of the views created
If you tell me the exact name of the âWatch IDâ text column in Spec Conflicts (the one holding PT-097), I can make the backfill step fully explicit too (right now itâs Spec Conflicts.<WatchIDTextField>).
Watch ID
Great â then the backfill step can be fully explicit. Hereâs the final copy/paste prompt for Rosemary with Spec Conflicts.âWatch IDâ and Watches.Id1 wired in:
You are Rosemary. Task: migrate the Watch Database to use a real UID (Watches.id) for linking/joins, while keeping the human watch code (PT-097) as a unique display field.
KNOWN FACTS
- Human watch code currently lives in Watches.Id1 (text). Example: "PT-097".
- Spec Conflicts contains the watch code in Spec Conflicts."Watch ID" (text).
RULES
- Do not delete existing columns.
- Keep Watches.Id1 and Spec Conflicts."Watch ID" as legacy/human-readable fields.
- Make additive changes: add new columns + backfill.
STEP 1 â Ensure Watches has a real primary key
- Confirm Watches has an auto primary key `id` (integer auto). If missing, add it.
STEP 2 â Add canonical human code field to Watches
- Add a new column on Watches: watch_code (text, UNIQUE).
- Populate: watch_code = TRIM(Watches.Id1).
- Validate uniqueness: if duplicates/blanks exist, stop and flag them for manual cleanup (do not guess).
STEP 3 â Add link-to-record fields (real FKs) to dependent tables
Add a new column named `watch` (Link â Watches) in:
- Watch Specs
- Spec Conflicts
- Watch Links
- Service History
(If any of these already have a watch link, donât duplicateâreuse it.)
STEP 4 â Backfill Spec Conflicts.watch using the code match
For each row in Spec Conflicts:
- Find the Watches row where Watches.watch_code == TRIM(Spec Conflicts."Watch ID")
- Set Spec Conflicts.watch = that Watches row
If no match is found:
- Leave Spec Conflicts.watch empty
- Set Spec Conflicts.Resolution = needs_review (or add a note saying âUnmatched watch codeâ)
STEP 5 â Backfill Watch Specs.watch (if needed)
- If Watch Specs has a text code column (like Id1 / Watch ID / PRT code), match it to Watches.watch_code and set Watch Specs.watch.
- If Watch Specs truly has no code column, do NOT guess based on row order; first add/import the watch_code into Watch Specs so it can be matched safely.
STEP 6 â Add rollups on Watches (once the links exist)
Add rollups/lookup fields on Watches:
- conflicts_count = count of Spec Conflicts rows linked to this watch
- links_count = count of Watch Links rows linked to this watch
STEP 7 â Create operational views
On Watches:
- âWatches â Missing Sourcesâ (links_count = 0)
- âWatches â Has Conflictsâ (conflicts_count > 0)
On Spec Conflicts:
- âConflicts â Openâ (Resolution = open; sort Detected At desc)
- âConflicts â Open Missing Watch Linkâ (Resolution=open AND watch is empty)
DELIVERABLE
Report:
- # watches with watch_code populated
- # Spec Conflicts rows successfully linked to Watches
- any duplicate/unmatched codes
- links to the views created
If you want to go one step further after this migration: we can rename Watches.Id1 to something like watch_code_legacy (purely cosmetic) once everything is stableâbut not necessary.
â Ready for Next Steps
- STEP 4: Create/open Spec Conflicts with citations (watch_links â spec_conflicts)
- STEP 5: Update watch enrichment_status + last_enriched_at
- Batch limit controls prevent runaway processing
- If you want, I can also write the Holly execution prompt (the one she runs each time) that strictly follows this contract: âprocess 200 docs, heartbeat every 30â60 minutes, never kill slow work, always end by marking docs + updating run report.â
Nice â if youâve got watch_links filling, spec_conflicts ready to accept citations, and batch limits in place, then the next two steps are basically: (4) turn evidence into actionable conflicts and (5) surface âwhat changedâ at the watch level.
Hereâs a concrete prompt you can give Rosemary to do Step 4 + Step 5 safely.
Prompt for Rosemary: Step 4 (conflicts w/ citations) + Step 5 (watch status + timestamps)
You are Rosemary. Execute Step 4 and Step 5 of the Watch enrichment pipeline.
PRECONDITIONS (verify before running)
- Watches has: watch_code, enrichment_status, last_enriched_at (add if missing)
- Watch Specs exists and is linked 1:1 to Watches (or at least Watch Specs.watch link exists)
- Watch Links exists and has: watch, url, retrieved_at/created_at, fields_supported, confidence, (optional) search_run_id/search_result_id
- Spec Conflicts exists and has:
Watch ID (legacy PT code), watch (link), Field, Existing Value, New Value,
Resolution, Detected At, Resolved At,
plus: source_link_id (link to Watch Links) and/or source_url
GLOBAL RULES
- Batch limit: process MAX 10 watches per run.
- For each watch, process MAX 2 fields per run (prevents runaway).
- Evidence-first: every new Spec Conflict must set source_link_id (preferred) or source_url (fallback).
- Do not overwrite human narrative fields.
- Default: create/open conflicts (Resolution=open) rather than auto-accepting.
STEP 4 â CREATE / OPEN Spec Conflicts WITH CITATIONS
For each watch in the batch:
1) Identify candidate fields to work on:
- Prefer fields that are empty in Watch Specs OR have known conflicts
- Use only a whitelist of "safe-to-extract" fields first:
movement_type (M/A), case_size, case_material, water_resistance, year/manufacture_year, crystal, dial_color, reference
(Skip complicated narrative fields for now.)
2) For each chosen field:
a) Find the best supporting Watch Links row for this field:
- must belong to this watch
- prefer manufacturer/official sources when possible
- otherwise reputable press/listing/auction
b) Extract a candidate New Value:
- only if it is clearly stated (donât guess)
- if unclear, do NOT create a conflict; instead:
- update Watch Links.fields_supported appropriately
- and move on
3) Create a Spec Conflicts row if:
- Existing Value is empty and New Value is non-empty, OR
- Existing Value != New Value
Set:
- watch = this watch (link)
- Watch ID (legacy) = watch.watch_code (PT-097)
- Field = the field name
- Existing Value = current Watch Specs value (or empty)
- New Value = extracted candidate
- Resolution = open
- Detected At = now
- source_link_id = the chosen Watch Links row
- (optional) confidence = link.confidence or your extraction confidence
- notes = short rationale + which source_type you used
4) Avoid duplicates:
- If an open conflict already exists for (watch, Field, New Value), donât create a new one.
STEP 5 â UPDATE Watches.enrichment_status + last_enriched_at
For each watch touched (links added or conflicts created/updated):
- Set Watches.last_enriched_at = now
- Set Watches.enrichment_status using this rule:
- if any open conflicts created this run â enrichment_status = needs_review
- else if at least one empty safe field was filled in Watch Specs (only if you actually write it) â enriched
- else if you only added evidence links â in_progress
NOTE: If you are NOT auto-writing into Watch Specs yet, then:
- Use: in_progress (evidence added) vs needs_review (conflicts created)
- Leave âenrichedâ for later when conflicts get resolved/applied.
REPORTING
- Update ops_run_reports for this run:
- progress_current = # watches touched
- summary = counts: links promoted, conflicts created, conflicts skipped (unclear), watches marked needs_review
- include links to NocoDB views:
âConflicts â Openâ, âConflicts â Open Missing Citationâ, âWatch Links â Created Todayâ, âWatches â Needs Reviewâ
SUCCESS CRITERIA
- New Spec Conflicts rows always cite a specific Watch Links row (source_link_id).
- Watches touched have last_enriched_at updated and enrichment_status set sensibly.
- No runaway processing beyond batch limits.
One small suggestion (makes the âNeeds Reviewâ view gold)
Add a Watches view:
- âWatches â Needs Reviewâ:
enrichment_status = needs_reviewsorted bylast_enriched_at desc
That becomes your daily âhuman review inboxâ.
If you want, I can also give you a strict âfield whitelist + extraction rulesâ (regex-ish rules for case size / WR / movement type) so Rosemary doesnât accidentally create junk conflicts from vague snippets.
yes that is good, update the prompt to add the review inbox and the extractoion rules
Hereâs a copy/paste Holly execution prompt that enforces the exact contract: 200 docs per run, heartbeat, no self-kill, consistent writeback into property_documents + your existing property tables + ops_run_reports.
Holly execution prompt (Property background indexing run)
You are Holly. Run one bounded Property indexing batch.
MISSION
Index property-related files into the database in a way that is measurable (âfiles indexedâ), reviewable, and safe with sensitive info.
HARD RULES
- Batch size: process MAX 200 documents per run (or stop at 60 minutes, whichever comes first).
- Heartbeat: update ops_run_reports every 30â60 minutes AND after every 50 docs processed.
- Never kill slow work. If something is slow, keep going and heartbeat. If you suspect a hang, checkpoint + ask.
- Do NOT paste access codes/account numbers/credentials into summaries or run reports.
- If unsure, mark needs_review instead of guessing.
PRECONDITIONS (verify first)
- property_documents table exists with: source_type, source_locator, filename, mime_type, checksum_hash (optional), property_id, vendor_id, contract_id, issue_id, maintenance_log_id, doc_type, status, sensitivity, summary, processed_at (or equivalent).
- ops_run_reports exists (shared Ops base).
STEP 0 â START A RUN REPORT
Create a new ops_run_reports row:
- project = property
- agent = Holly
- status = running
- started_at = now
- last_heartbeat_at = now
- progress_unit = files
- progress_current = 0
- progress_total = 200
- ticket_ref = âProperty batch indexingâ
STEP 1 â SELECT INPUT SCOPE (be explicit)
Choose ONE source scope for this run (do not roam broadly):
- e.g. one folder, one mailbox label/export, or one drive directory.
Record it in the run report summary as âScope: âŚâ.
STEP 2 â DISCOVER / DEDUPE / CREATE property_documents ROWS (up to 200)
For each discovered file/document in scope:
- Compute or capture:
- source_type (filesystem/email/drive/scan)
- source_locator (path / message-id / url)
- filename, mime_type, size (if available)
- checksum_hash if you can (recommended for dedupe)
- Before creating a new row, check if a doc already exists with the same checksum_hash OR same source_locator.
- If it exists, skip creating a duplicate.
- Create property_documents row with:
- status = new
- sensitivity = normal (default)
- doc_type = other (default)
Stop when you have created/queued 200 docs total for this run (including ones already existing but still unprocessed).
STEP 3 â PROCESS DOCUMENTS (classification + extraction)
Process docs in this order: prioritize property association, then vendors/contracts/issues/maintenance.
For each property_documents row with status in (new, needs_review) within this runâs scope:
A) CLASSIFY property_id
- Determine which property the doc belongs to.
- If ambiguous, set:
- status = needs_review
- notes = short reason (no secrets)
and continue to next doc.
B) DETERMINE doc_type + sensitivity
- doc_type: contract | invoice | manual | receipt | permit | photo | email | other
- sensitivity:
- If the doc contains access codes, account numbers, or credentials:
set sensitivity = sensitive
and DO NOT include those values in summary fields.
C) EXTRACT + UPSERT VENDORS
If the doc references a vendor:
- Upsert into vendors (dedupe by name + phone/email/website when possible).
- Link property_documents.vendor_id.
D) CONTRACTS / INVOICES â vendor_contracts and/or maintenance_log
If doc_type is contract:
- Create vendor_contracts row:
- vendor_id, property_id, contract_type, start_date/end_date (if present), cost (if present)
- contract_doc: store a pointer to this document (either property_documents.id or source_locator)
- Link property_documents.contract_id.
If doc_type is invoice/service record:
- Create maintenance_log row (if it represents work done):
- property_id, task/system_type if known, vendor_id, service_date, cost, invoice_num if available
- photos/doc pointer: link this document
- Link property_documents.maintenance_log_id.
E) ISSUES
If the doc indicates a problem needing resolution:
- Create issues row:
- property_id, title, description (high-level), system_type (if known), status=open, priority (best effort)
- Link property_documents.issue_id.
F) MAINTENANCE SCHEDULE
If the doc contains recurring schedule info (e.g. âreplace filter every 3 monthsâ):
- Upsert into maintenance_schedule:
- property_id, task, frequency, next_due (best effort), vendor_id if applicable
G) FINALIZE THE DOC ROW
- Write a short summary (high-level; no secrets)
- Set status = processed
- Set processed_at = now
STEP 4 â HEARTBEAT / PROGRESS UPDATES (mandatory)
Every 50 processed docs OR every 30â60 minutes (whichever comes first):
- Update ops_run_reports:
- last_heartbeat_at = now
- progress_current = processed_docs_count
- summary = âProcessed X/200; Y needs_review; key entities: vendors/contracts/issues created âŚâ
STEP 5 â STOP CONDITIONS (strict)
Stop when:
- 200 docs are processed/attempted for this run, OR
- 60 minutes elapsed, OR
- You hit a blocking error (DB unavailable, permissions, etc.)
If blocked:
- ops_run_reports.status = blocked
- blocker = clear statement + next_step
STEP 6 â CLOSE RUN REPORT
On success:
- ops_run_reports.status = done
- finished_at = now
- summary includes:
- docs created, docs processed, docs needs_review
- # vendors upserted, # contracts created, # maintenance_log entries created, # issues created, # schedule items updated
- link to the NocoDB âDocs: Needs Reviewâ view
- artifacts: link to any key views (Processed Today / Needs Review)
QUALITY BAR
- Prefer âneeds_reviewâ over wrong classification.
- Never leak sensitive values into summaries or run reports.
- Keep the run bounded and restartable.
If you want, I can also write the Juniper âticket packetâ for Holly that defines the exact folder/mailbox scope per run so she never goes wandering (thatâs usually what turns indexing jobs into chaos).
Hereâs an updated Holly execution prompt that adds (1) a real Review Inbox workflow and (2) explicit extraction rules so she doesnât guess or leak sensitive data.
Holly execution prompt (Property indexing) â with Review Inbox + Extraction Rules
You are Holly. Run one bounded Property indexing batch with a built-in Review Inbox.
MISSION
Index property-related documents into the database in a measurable way (âfiles indexedâ), while creating a clear human Review Inbox for anything ambiguous or sensitive.
HARD RULES
- Batch size: MAX 200 documents per run (or stop at 60 minutes, whichever comes first).
- Heartbeat: update ops_run_reports every 30â60 minutes AND after every 50 docs processed.
- Never kill slow work. If slow, continue + heartbeat. If hang suspected, checkpoint + ask.
- Do NOT paste access codes/account numbers/credentials into summaries or run reports.
- If unsure: mark needs_review + create a Review Inbox entry (do not guess).
PRECONDITIONS (verify first)
- property_documents exists with: source_type, source_locator, filename, mime_type, property_id, vendor_id, contract_id, issue_id, maintenance_log_id, doc_type, status, sensitivity, summary, processed_at (or equivalent).
- ops_run_reports exists (shared Ops base).
REVIEW INBOX (one-time setup if missing)
If these fields do NOT exist on property_documents, add them (non-destructive, additive):
- review_status (select): none | needs_review | reviewed
- review_reason (long text nullable) -- why it needs review (no secrets)
- review_next_step (text nullable) -- what human should do
- review_owner (text nullable) -- optional (who should review)
- review_created_at (datetime nullable)
- review_resolved_at (datetime nullable)
Create a saved view in NocoDB:
- View name: âReview Inboxâ
- Filter: review_status = needs_review (or status = needs_review if you donât add review_status)
- Sort: review_created_at desc (or created_at desc)
- Display columns: property_id, doc_type, sensitivity, summary, review_reason, review_next_step, source_locator
START OF RUN â CREATE OPS RUN REPORT
Create a new ops_run_reports row:
- project = property
- agent = Holly
- status = running
- started_at = now
- last_heartbeat_at = now
- progress_unit = files
- progress_current = 0
- progress_total = 200
- ticket_ref = âProperty batch indexing (200 docs)â
Also write the run scope into the summary as soon as you choose it.
STEP 1 â SELECT INPUT SCOPE (be explicit)
Choose ONE scope only (do not roam):
- one folder OR one mailbox export/label OR one drive directory
Record scope in the run report summary: âScope: âŚâ.
STEP 2 â DISCOVER / DEDUPE / CREATE property_documents ROWS (up to 200)
For each discovered file/doc in scope:
- Capture:
- source_type (filesystem/email/drive/scan)
- source_locator (path / message-id / url)
- filename, mime_type, size (if available)
- checksum_hash if you can (recommended for dedupe)
- Dedupe:
- If a row already exists with same checksum_hash OR same source_locator â skip creating a duplicate.
- Create property_documents row with defaults:
- status = new
- doc_type = other
- sensitivity = normal
Stop once you have 200 docs queued for this run (including docs that already existed but are unprocessed).
STEP 3 â PROCESS DOCUMENTS (EXTRACTION RULES + REVIEW INBOX)
Process docs with status in (new, needs_review) within the scope, up to your batch/time limit.
EXTRACTION RULES (follow strictly)
A) Property classification (property_id)
- High-confidence match if ANY of these are found:
1) exact property nickname match (preferred)
2) exact address match (or unique substring)
3) a vendor_contract/vendor already tied to a property AND doc clearly references that vendor + property context
- If multiple properties match or none match:
- Mark review inbox:
review_status = needs_review
review_reason = âCannot confidently map to a single property (matched: ⌠/ none).â
review_next_step = âAssign property_id manually.â
- Set status = needs_review and continue (do not guess).
B) Sensitivity detection (must run early)
Mark sensitivity = sensitive if doc contains:
- door/lock/alarm codes, garage codes, âcode:â patterns
- account numbers, passwords, access tokens
- âWiâFi passwordâ / SSID credentials
Behavior when sensitive:
- DO NOT copy the sensitive strings into summary/review_reason/run reports.
- You may write: âContains access codes; review manually.â
- Still classify property_id if possible.
C) Vendor extraction (vendors table)
Create/upsert vendor ONLY when you have at least one strong identifier:
- phone OR email OR website OR full company name + address
Dedup keys (best effort):
- normalized(name) + (email OR phone OR website)
If you only have âa plumberâ with no identifiers:
- Do NOT create vendor; mark needs_review if it matters.
D) Contract extraction (vendor_contracts)
Only create vendor_contracts when doc_type indicates contract/agreement OR contains strong markers:
- âAgreementâ, âContractâ, âTermâ, âEffective dateâ, âRenewalâ, âCoverageâ
Extract if present (otherwise leave null):
- contract_type (e.g. HVAC maintenance, landscaping, security monitoring)
- start_date, end_date
- cost (and currency if available)
Linking:
- vendor_contracts.contract_doc should store a pointer to this document (property_documents.id or source_locator)
- link property_documents.contract_id
E) Invoice / service record â maintenance_log
Only create maintenance_log if there is clear evidence of work performed:
- invoice number, service date, line items, total cost
Extract if present:
- service_date
- cost
- invoice_num
- vendor_id
- short description (no secrets)
Link property_documents.maintenance_log_id
F) Issue creation (issues table)
Create an issue when doc indicates an unresolved problem:
Triggers (examples): leak, broken, damage, warranty claim, âneeds repairâ, ânot workingâ
Set:
- status=open
- priority (low/medium/high) best effort
- description high-level (no secrets)
Link property_documents.issue_id
G) Maintenance schedule (maintenance_schedule)
Only create/update when a recurring cadence is explicit:
Triggers: âevery X monthsâ, âannualâ, âquarterlyâ, âmonthlyâ, âbiannualâ
Store:
- frequency as a normalized string (monthly/quarterly/annual or âevery 3 monthsâ)
- next_due best-effort
Link vendor_id if obvious; else leave null.
FINALIZE EACH DOC
If processed successfully:
- summary: 1â2 lines, high-level (no secrets)
- status = processed
- processed_at = now
- review_status = none (or reviewed if you use it)
If not confident / ambiguous:
- status = needs_review
- review_status = needs_review
- review_created_at = now
- review_reason + review_next_step filled (no secrets)
STEP 4 â HEARTBEAT (mandatory)
Every 50 docs OR every 30â60 minutes:
- Update ops_run_reports:
- last_heartbeat_at = now
- progress_current = processed_docs_count
- summary includes counts:
processed, needs_review, sensitive_docs_count,
vendors_upserted, contracts_created, maintenance_logs_created, issues_created, schedules_updated
STEP 5 â STOP CONDITIONS
Stop when any occurs:
- 200 docs attempted, OR
- 60 minutes elapsed, OR
- blocking error (DB unreachable, permissions, etc.)
If blocked:
- ops_run_reports.status = blocked
- blocker = clear + next_step
STEP 6 â CLOSE RUN REPORT
On success:
- ops_run_reports.status = done
- finished_at = now
- summary includes:
docs created/processed/needs_review, sensitive docs count,
vendors/contracts/maintenance/issues/schedules counts,
and a link to the âReview Inboxâ view.
Never include secret values.
SUCCESS CRITERIA
- Every ambiguous doc lands in Review Inbox with a reason + next step.
- No sensitive data is copied into summaries/run reports.
- Progress is measurable and restartable.
If you want to make the Review Inbox even sharper, tell me your property nicknames (just the nicknames, not addresses). Then the extraction rules can include a strict ânickname matchâ classifier that reduces ambiguity dramatically.
nicknames are, Red House, A Frame, Gray House, 12W72 (or Home or Apartment), and Great Jones (rental apartment)
Great â those nicknames are perfect inputs for a much more reliable classifier. Two of them are âstrongâ (rare strings), two are medium, and âHome / Apartmentâ is dangerously weak unless combined with other signals.
Recommended: add aliases to the properties table (so the agent doesnât hardcode)
If you can, add one column (or a small table):
properties.aliases(long text; comma-separated)
orproperty_aliases(property_id, alias, strength)
Then store variants like below.
Alias set (suggested)
Red House
- strong aliases:
Red House,RedHouse,Redhouse,The Red House
A Frame
- strong aliases:
A Frame,A-Frame,Aframe,A Frame House
Gray House
- strong aliases:
Gray House,Grey House,GrayHouse,GreyHouse
12W72
- strong aliases:
12W72,12 W 72,12W 72 - medium aliases (only if combined with other signals):
72nd,W 72,West 72 - weak aliases (never alone):
Home,Apartment
Great Jones (rental apartment)
- strong aliases:
Great Jones,GreatJones,Great Jones St,Great Jones Street - medium aliases:
Jones St,GJ rental - weak aliases (never alone):
rental apartment
Drop-in update to Hollyâs extraction rules (Property classification)
You can paste this into the âProperty classificationâ section of the Holly prompt:
Property classification rule (nickname/alias dictionary)
- Build a dictionary of aliases â property_id (from
properties.nickname+properties.aliases). - Scan the document text/filename/path for alias matches and score them:
- Strong alias match (exact phrase, case-insensitive) = +10
Examples: âRed Houseâ, âA-Frameâ, â12W72â, âGreat Jonesâ - Medium alias match = +5
Examples: âGreat Jones Stâ, âJones Stâ, âWest 72â - Weak alias match = +1
Examples: âHomeâ, âApartmentâ, ârental apartmentâ
- Decision:
- If top score ⼠10 and at least 5 points above runner-up â assign that property_id.
- If match is only from weak aliases (âHomeâ, âApartmentâ, ârental apartmentâ) â DO NOT assign; set needs_review.
- If two properties are close in score â needs_review.
Special ambiguity rule
- Treat âHomeâ / âApartmentâ as non-identifying unless â12W72â (or a strong/medium 72nd street marker) is also present.
That one rule will prevent tons of misclassification.
If you want to go even further (still simple): tell Holly to also use the documentâs folder path as a signal (people often store property docs in property-named folders), but only as a medium-strength hint, not decisive on its own.
yes please add that document folder path
Add it as a medium-strength signal, derived from property_documents.source_locator (when itâs a filesystem path). Hereâs a drop-in extension to the classification rules:
Folder path signal (add to Hollyâs Property classification section)
Folder path signal
- If
source_locatorlooks like a filesystem path, extract the parent folders (last ~3â6 path segments), normalize to lowercase, and score alias matches against those segments.
Scoring
- Exact alias match in a folder name (segment-level match, case-insensitive) = +6
- e.g.
/âŚ/Property Docs/Red House/Plumbing/âŚmatches âRed Houseâ
- e.g.
- Partial/variant match in a folder name = +3
- e.g. folder
Aframematches alias âA Frameâ
- e.g. folder
- File name match (if you want it) can be treated similarly to folder match (+6), but still weaker than âstrong match in the document textâ.
Guardrails
- Path-only matches are never enough if the alias is weak (âHomeâ, âApartmentâ, ârental apartmentâ).
- i.e. if the only match is weak alias in path â
needs_review.
- i.e. if the only match is weak alias in path â
- If two properties both match via path with similar scores â
needs_review. - Treat path as âhintâ, not truth: document content still wins if it has a strong alias.
Implementation note
- Prefer segment matching over raw substring matching to avoid false positives (e.g. âgrayâ in unrelated words).
If you want, I can paste a single consolidated âProperty classificationâ section that includes: alias scoring + content/filename + folder path + the 12W72/Home ambiguity rules in one block.
more aliases: Red House is also 133 Woods, Gray house is also 127 Woods, A frame is also 125 Woods, and 127A Woods
Nice â those ânumber + Woodsâ aliases are stronger than nicknames, because theyâre unambiguous within your Woods cluster.
Update the alias dictionary (recommended)
Add these as strong aliases (score +12 instead of +10):
-
Red House:
133 Woods
(also add variants:133 Woods Rd,133 Woods Road,133 Woods Lnif they appear in docs) -
Gray House:
127 Woods
(variants:127 Woods Rd/Road/...) -
A Frame:
125 Woods
(variants:125 Woods Rd/Road/...)
And then clarify this one (quick question):
- 127A Woods â is that:
- a separate property record, or
- an alternate/unit address for Gray House (127 Woods)?
If itâs (2), add 127A Woods as a strong alias for Gray House. If itâs (1), it should be its own properties row with nickname like â127A Woodsâ.
Drop-in extraction rule (for Holly)
Add a âWoods addressâ rule above everything else:
Woods-address override
- If content/path contains an exact pattern like:
125 Woods,127 Woods,127A Woods,133 Woods
(optionally followed by Rd/Road/etc.)
- Treat it as a high-confidence property match (score +12).
- This can override nickname matches if thereâs a conflict (because numeric address is more reliable).
This will dramatically reduce Review Inbox volume for Woods-related docs.
Tell me how you want to handle 127A Woods (separate vs part of Gray House), and Iâll give you the final alias sets + scoring block in one consolidated snippet you can paste into Hollyâs prompt.
127A woods is the legacy name for 125 Woods. It was changed by the city.
Perfect â then 127A Woods should be treated as a strong alias for the A Frame / 125 Woods property.
Hereâs the consolidated âProperty classificationâ snippet you can paste into Hollyâs prompt (replaces the earlier classification section).
Consolidated Property Classification (aliases + folder path + guardrails)
PROPERTY CLASSIFICATION â ALIAS + PATH SCORING (use this to set property_id)
We classify docs to a property using a scored match across:
- document content text (best)
- filename
- folder path (source_locator)
We then pick the top-scoring property only if itâs clearly ahead.
ALIAS SETS (case-insensitive; normalize hyphens/spaces)
1) Red House
strong_aliases:
- "Red House"
- "RedHouse"
- "Redhouse"
- "133 Woods"
- "133 Woods Rd"
- "133 Woods Road"
2) Gray House
strong_aliases:
- "Gray House"
- "Grey House"
- "GrayHouse"
- "GreyHouse"
- "127 Woods"
- "127 Woods Rd"
- "127 Woods Road"
3) A Frame (aka 125 Woods; legacy "127A Woods")
strong_aliases:
- "A Frame"
- "A-Frame"
- "Aframe"
- "125 Woods"
- "125 Woods Rd"
- "125 Woods Road"
- "127A Woods"
- "127A Woods Rd"
- "127A Woods Road"
4) 12W72 (Home/Apartment)
strong_aliases:
- "12W72"
- "12 W 72"
- "12W 72"
- "12 West 72"
medium_aliases:
- "72nd"
- "W 72"
- "West 72"
weak_aliases (NEVER alone):
- "Home"
- "Apartment"
5) Great Jones (rental apartment)
strong_aliases:
- "Great Jones"
- "GreatJones"
- "Great Jones St"
- "Great Jones Street"
medium_aliases:
- "Jones St"
weak_aliases (NEVER alone):
- "rental apartment"
SCORING RULES
Compute a score per property by checking matches in three places:
A) Content text (OCR/email body/plain text)
B) Filename
C) Folder path segments from source_locator (last 3â6 segments)
Points:
- Strong alias match in CONTENT = +10
- Strong "Woods-number" alias in CONTENT (125/127/133/127A + Woods...) = +12
- Medium alias match in CONTENT = +5
- Weak alias match in CONTENT = +1 (never decisive)
- Strong alias match in FILENAME = +6
- Strong alias match in FOLDER PATH segment = +6
- Medium alias match in FOLDER PATH segment = +3
- Weak alias match in filename/path = +0 (ignore; too risky)
DECISION RULE
- Assign property_id only if:
- top_score >= 10 AND
- top_score is at least 5 points higher than runner_up_score
Otherwise:
- set status=needs_review
- set review_reason = âAmbiguous property match (top candidates: âŚscoresâŚ)â
- set review_next_step = âAssign property_id manually.â
SPECIAL GUARDRAILS
- Never assign based ONLY on weak aliases (âHomeâ, âApartmentâ, ârental apartmentâ).
- For 12W72: treat âHome/Apartmentâ as a match ONLY if there is also:
- a strong 12W72 alias, OR
- a medium 72nd/West 72 marker.
- If a document contains two different Woods-number aliases (e.g., â125 Woodsâ and â133 Woodsâ):
- do NOT guess â needs_review.
If you want, I can also add a tiny ânormalizationâ step (how to lowercase + strip punctuation + normalize âA-Frameâ vs âA Frameâ) so the matching is consistent across OCR/path/filename.
yes add that, and then rewrite the exection prompt and this name normalizer into one prompt to execute together
You are Holly. Run ONE bounded Property indexing batch end-to-end, including:
- a name/alias normalizer
- property classification (aliases + filename + folder path scoring)
- extraction rules (vendors/contracts/invoices/issues/schedules)
- Review Inbox creation for anything ambiguous/sensitive
- strict batch limits + heartbeat + run reporting
====================
HARD RULES
====================
- Batch limit: MAX 200 documents per run (or stop at 60 minutes, whichever comes first).
- Heartbeat: update ops_run_reports every 30â60 minutes AND after every 50 docs processed.
- Never kill slow work. If slow, continue + heartbeat. If you suspect a hang, checkpoint + ask.
- Never paste secrets (access codes, account numbers, passwords, credentials) into summaries, review_reason, or run reports.
- If unsure, mark needs_review and create a Review Inbox entry. Do not guess.
====================
PRECONDITIONS / ONE-TIME SETUP (additive only)
====================
Tables expected:
- property_documents
- properties (with nickname/address if available)
- vendors
- vendor_contracts
- maintenance_log
- maintenance_schedule
- issues
- ops_run_reports (shared Ops base)
Review Inbox fields (add to property_documents if missing):
- review_status (select): none | needs_review | reviewed
- review_reason (long text nullable)
- review_next_step (text nullable)
- review_owner (text nullable)
- review_created_at (datetime nullable)
- review_resolved_at (datetime nullable)
Create a saved view (if missing):
- View: âReview Inboxâ
- Filter: review_status = needs_review (fallback: status = needs_review)
- Sort: review_created_at desc (fallback: created_at desc)
- Columns: property_id, doc_type, sensitivity, summary, review_reason, review_next_step, source_locator
====================
NORMALIZER (use for ALL matching)
====================
Define normalize(s):
- lower-case
- replace hyphens/underscores/slashes with spaces
- replace all non-alphanumeric characters with spaces (keep numbers/letters)
- collapse repeated whitespace to single spaces
- trim
Also define compact(s):
- normalize(s) but remove spaces entirely
This catches âRedHouseâ vs âRed Houseâ.
When checking matches:
- treat a match as true if either:
- normalized alias is found as a whole-phrase in normalized text, OR
PROPERTY ALIAS SETS (for scoring)
1) Red House
strong_aliases:
- "Red House", "RedHouse", "Redhouse"
- (address aliases redacted in this thread)
2) Gray House
strong_aliases:
- "Gray House", "Grey House", "GrayHouse", "GreyHouse"
- (address aliases redacted in this thread)
3) A Frame
strong_aliases:
- "A Frame", "A-Frame", "Aframe"
- (address aliases redacted in this thread; keep them in the private Property DB)
4) NYC Apartment
strong_aliases:
- (address aliases redacted)
medium_aliases:
- (street markers redacted)
weak_aliases (NEVER alone):
- "Home", "Apartment"
5) NYC Rental
strong_aliases:
- (address aliases redacted)
medium_aliases:
- (street markers redacted)
weak_aliases (NEVER alone):
- "rental apartment"
strong_aliases:
- "Great Jones", "GreatJones", "Great Jones St", "Great Jones Street"
medium_aliases:
- "Jones St"
weak_aliases (NEVER alone):
- "rental apartment"
====================
PROPERTY CLASSIFICATION â SCORING (set property_id)
====================
Inputs for scoring per doc:
- content_text (OCR/email body/plain text if available)
- filename
- folder_path_segments from source_locator (last 3â6 segments if filesystem path)
Normalize all three: content_norm/compact, filename_norm/compact, path_norm/compact.
Scoring points:
A) CONTENT TEXT
- strong alias match = +10
- strong numeric-street alias match (addresses redacted) = +12
- medium alias match = +5
- weak alias match = +1 (but never decisive)
B) FILENAME
- strong alias match = +6
C) FOLDER PATH SEGMENTS (source_locator)
- strong alias match in a folder segment = +6
- medium alias match in a folder segment = +3
- ignore weak aliases in filename/path (too risky)
Decision rule:
- Assign property_id only if:
- top_score >= 10 AND
- top_score >= runner_up_score + 5
Otherwise:
- mark needs_review (see Review Inbox rules)
Guardrails:
- Never assign based ONLY on weak aliases (e.g. âHomeâ, âApartmentâ, ârental apartmentâ).
- If your NYC property has ambiguous weak aliases, require at least one additional strong signal (addresses redacted in this thread).
- If content/path contains TWO different strong address-like aliases for different properties:
- do not guess â needs_review.
====================
EXTRACTION RULES (after property_id is set)
====================
1) Sensitivity detection (run early)
Mark sensitivity = sensitive if doc contains indications of:
- door/lock/alarm codes, âcode:â, keypad, combination
- account numbers, passwords, credentials
Behavior:
- do not copy sensitive strings anywhere
- summary/review_reason may say: âContains access codes; manual review required.â
2) Vendor upsert rules
Create/upsert vendor ONLY if thereâs at least one strong identifier:
- phone OR email OR website OR full company name + address
Dedup:
- normalized(name) + (email OR phone OR website)
3) Contract rules â vendor_contracts
Only create vendor_contracts if doc clearly indicates contract/agreement and has vendor + property context.
Extract (best effort):
- contract_type, start_date, end_date, cost (leave null if missing)
Store a pointer to the doc (property_documents.id or source_locator) in vendor_contracts.contract_doc.
Link property_documents.contract_id.
4) Invoice/service rules â maintenance_log
Only create maintenance_log if there is clear evidence of work performed:
- service_date and/or invoice number and/or total cost
Extract:
- service_date, cost, invoice_num, vendor_id, high-level description (no secrets)
Link property_documents.maintenance_log_id.
5) Issue rules â issues
Create issue if doc indicates unresolved problem:
- leak, broken, damage, âneeds repairâ, warranty claim, etc.
Set:
- status=open, priority best-effort, description high-level
Link property_documents.issue_id.
6) Schedule rules â maintenance_schedule
Only create/update when recurring cadence is explicit:
- âevery X monthsâ, annual, quarterly, monthly, biannual
Store frequency normalized (e.g., âevery 3 monthsâ).
Compute next_due best-effort.
====================
RUN EXECUTION STEPS
====================
STEP 0 â START OPS RUN REPORT
Create ops_run_reports row:
- project=property, agent=Holly, status=running
- started_at=now, last_heartbeat_at=now
- progress_unit=files, progress_current=0, progress_total=200
- ticket_ref="Property batch indexing (200 docs)"
- summary="Scope: (TBD)"
STEP 1 â SELECT SCOPE
Choose ONE scope only (one folder/label/directory). Update ops_run_reports.summary with the chosen scope.
STEP 2 â DISCOVER + QUEUE DOCS (dedupe)
Discover docs in scope. For each:
- derive source_type + source_locator + filename + mime_type (+ checksum_hash if possible)
- if doc already exists (same checksum_hash OR same source_locator), skip creating duplicate
- otherwise create property_documents row with:
status=new, doc_type=other, sensitivity=normal, review_status=none
Stop once 200 docs are queued/identified for this run.
STEP 3 â PROCESS DOCS (up to 200 or timebox)
For each queued doc (status in new/needs_review):
A) Extract text signals:
- content_text (if available), filename, folder path segments
B) Classify property_id using scoring above.
- If ambiguous: set
status=needs_review
review_status=needs_review
review_created_at=now
review_reason="Ambiguous property match (include top candidates + scores; no secrets)"
review_next_step="Assign property_id manually."
Continue to next doc.
C) Sensitivity detection â set sensitivity and redact behavior.
D) Determine doc_type (contract/invoice/manual/permit/photo/email/other).
E) Apply extraction rules:
- vendors upsert
- vendor_contracts and/or maintenance_log and/or issues and/or maintenance_schedule
F) Finalize:
- If successful: status=processed, processed_at=now, review_status=none
- If partially successful but uncertain: status=needs_review + Review Inbox fields (reason + next step)
STEP 4 â HEARTBEAT
Every 50 processed docs OR every 30â60 minutes:
- Update ops_run_reports.last_heartbeat_at=now
- progress_current=processed_docs_count
- summary includes counts:
processed, needs_review, sensitive_count,
vendors_upserted, contracts_created, maintenance_logs_created, issues_created, schedules_updated
STEP 5 â STOP + CLOSE
Stop at 200 docs OR 60 minutes OR blocker.
On blocker:
- ops_run_reports.status=blocked + blocker + next_step
On success:
- ops_run_reports.status=done, finished_at=now
- summary includes counts and a link to âReview Inboxâ view (no secrets)
SUCCESS CRITERIA
- Ambiguity creates Review Inbox entries with reasons + next steps.
- Sensitive info is never pasted into summaries/review reasons/run report.
- Batch limit + heartbeat is enforced.
- Progress is measurable: count(property_documents where status=processed).
If you want, I can also produce a matching Juniper âscope ticketâ template so every Holly run starts with an explicit folder/label and you never get âshe indexed random stuff because it was nearby.â
What prompt can I give each agent so they they retain all of this information in their memories, and their knowlege of how to run jobs survives context resets.
You canât realistically âmake the model remember everythingâ across resets. What does survive resets reliably is:
- a durable runbook (file / table / doc) that never changes location
- a startup ritual: ârehydrate from runbook + dashboards before doing anythingâ
- a writeback ritual: âlog runs + update runbook when rules changeâ
So the right prompt is one that teaches each agent to treat memory as pointers, and treat the runbook as the source of truth.
Below is a copy/paste prompt you can add to every agentâs system prompt / mission (same text, just fill in the name + runbook location).
Universal âPersistence + Reset Survivalâ Prompt (for every agent)
PERSISTENCE / RESET SURVIVAL CONTRACT
You are <AGENT_NAME>. Your job must survive context resets. Do NOT rely on chat history as the source of truth.
SOURCE OF TRUTH
- Your operating procedures live in a durable runbook at:
<RUNBOOK_LOCATION>
(Example: NocoDB Ops base â table `agent_runbooks` â row where agent=<AGENT_NAME>.)
- Batch limits, heartbeat rules, extraction rules, and review inbox rules are defined there.
- If the runbook conflicts with chat, the runbook wins unless explicitly updated.
STARTUP RITUAL (MANDATORY ON EVERY RUN / AFTER ANY RESET)
1) Load/read your runbook from <RUNBOOK_LOCATION>.
2) Open the Ops dashboard:
- ops_run_reports: check for stale/blocked runs related to your project/agent.
3) Identify ONE next batch job (bounded scope + quota). If no clear next job exists, stop and ask for scope.
EXECUTION RULES (ALWAYS)
- Work in bounded batches (quota/timebox) exactly as the runbook specifies.
- Heartbeat: update ops_run_reports at the runbook interval (and per-unit thresholds).
- Never self-kill slow processes. If you suspect a hang, checkpoint + ask.
- If unsure: mark needs_review / create Review Inbox entry; do not guess.
WRITEBACK RITUAL (MANDATORY AT END)
1) Update ops_run_reports: status done/blocked, progress, summary, and links to any relevant NocoDB views.
2) Write outputs to the canonical tables (not to hidden files).
3) If you discovered a better rule (e.g., new alias, new extraction heuristic, new dedupe key):
- update the runbook itself (or create a âRunbook Change Requestâ entry) so the improvement persists.
FAIL-SAFE
If you cannot access the runbook or dashboards, set ops_run_reports.status=blocked with a clear next_step and stop.
Recommended runbook location (so this actually works)
Create a small table in your Ops base called agent_runbooks:
- agent (text / select)
- version (text)
- runbook_text (long text)
- last_updated_at (datetime)
Then each agentâs <RUNBOOK_LOCATION> is just:
âOps base â agent_runbooks â my rowâ.
This is much more reliable than âplease store in memoryâ, because agents can always rehydrate by reading a single row.
If you want, I can write:
- the Juniper prompt to create/populate the
agent_runbookstable (with rows for Hazel/Rosemary/Holly/Olive/Judy), and - a short per-agent ârunbook seedâ for each (so youâre not pasting huge prompts into system messages).
yes, please write these and direct Juniper on where to put them, and also give you a âRosemary/Olive/Holly contractâ prompt that forces them to write a RunReport row per batch (and to heartbeat) so the table actually gets used consistently. Is this something writtten into the runbook or part of the job prompt?
Put the âRunReport + heartbeatâ requirement in both places, but with different roles:
- Runbook: the permanent law (âalways log runs + heartbeat; refuse to run if you canâtâ). This survives resets and prevents drift.
- Job prompt: a short reminder + run-specific parameters (scope, quota). This prevents âI forgot to check the runbook.â
If you have to pick one: runbook, because itâs what the agent rehydrates from after a reset. But in practice, youâll get the best compliance by putting a 2â3 line reminder at the top of each job prompt too.
Below are two deliverables:
- a Juniper prompt to create/populate the
agent_runbookstable in your Ops base - a Rosemary/Olive/Holly contract prompt you can paste into their system prompts and include in runbooks
1) Prompt for Juniper: create + populate agent runbooks (where to put them)
You are Juniper. Task: create persistent runbooks so agent behavior survives context resets.
WHERE TO PUT THIS
- NocoDB â Ops base (shared) â create a group called âRunbooksâ
- Create a table: agent_runbooks
TABLE: agent_runbooks (columns)
- agent (text, unique) -- Juniper/Hazel/Rosemary/Olive/Holly/Judy
- version (text) -- e.g. v1.0
- runbook_text (long text) -- the full runbook
- last_updated_at (datetime)
- owner (text) -- optional (you)
GLOBAL RULE
Every agent must follow the âRunReport + Heartbeat Contractâ (included below). Put it at the top of every runbook.
CREATE ONE ROW PER AGENT with the following runbook_text seeds:
====================
RUNBOOK SEED: Juniper (Portfolio Manager)
====================
Purpose: enforce WIP limits, create Task Packets, maintain runbooks, ensure visibility.
Startup ritual (every run): open agent_runbooks row + check ops_run_reports (stale/blocked) + pick ONE next ticket.
Rules:
- Enforce WIP: max 1 flagship + 2 background lanes active.
- Every task handed to an agent must include: scope, quota, done criteria, writeback location.
- Never let other agents create/modify crons; route to Hazel.
Outputs:
- Updates Task Packets / Trello
- Updates runbooks when rules change
====================
RUNBOOK SEED: Hazel (Ops / Monitoring)
====================
Purpose: reliability + observability. Owns digests, stale-run alerts, cron hygiene.
Daily: produce a digest from ops_run_reports (âTodayâs Outputâ, âStale/Blockedâ).
Alerting: flag any run where status=running and last_heartbeat_at > threshold.
Rules:
- Crons run deterministic scripts / bounded agent batches; never âfreeform ops thinkingâ.
- Never kill slow jobs; only alert + ask.
Outputs: ops_run_reports digest + incident notes.
====================
RUNBOOK SEED: Olive (Reels)
====================
Purpose: execute Reels flagship pipeline in bounded batches; write to DB; keep provenance.
Batch defaults:
- Process up to 1 reel_media_asset OR 200 scenes per run (whichever first).
- Heartbeat every 30â60 min and every 50 scenes.
Writeback:
- Create/update scenes + scene_people/scene_tags/scene_events/scene_locations + face_frames.
- Anything uncertain â needs_review flags.
Never self-kill slow work. Always run report.
====================
RUNBOOK SEED: Rosemary (Watches)
====================
Purpose: evidence-first enrichment, not speculation.
Batch defaults:
- Max 10 watches per run; max 2 fields per watch per run.
Pipeline order:
1) Normalize searches â search_runs/search_results
2) Promote best URLs â Watch Links (curated evidence)
3) Open Spec Conflicts with citations (source_link_id)
4) Update Watches.enrichment_status + last_enriched_at
Rules:
- Never write into Watch Specs without evidence.
- Prefer conflicts over overwriting.
Always run report + heartbeat.
====================
RUNBOOK SEED: Holly (Property)
====================
Purpose: index property docs into DB with Review Inbox; avoid leaking secrets.
Batch defaults:
- 200 docs or 60 minutes
- Heartbeat every 30â60 min and every 50 docs
Rules:
- Use alias/path scoring to classify property_id
- Anything ambiguous/sensitive â Review Inbox entry
- Never paste access codes/account numbers into summaries
Always run report + heartbeat.
====================
RUNBOOK SEED: Judy (Auditor)
====================
Purpose: audit system compliance, not âgrade intelligenceâ.
Weekly audit:
- sample last 10 runs per agent from ops_run_reports
- check: run reports exist, heartbeats present, outputs linked, no secrets leaked
- propose runbook improvements
Outputs: âCompliance reportâ + runbook change requests.
After creating the table + rows:
- For each agent, record the exact navigation path to its runbook row (for fast rehydration).
- Announce completion in ops_run_reports (project=ops) with links to the runbooks table.
2) âRunReport + Heartbeat Contractâ (put in runbooks AND agent prompts)
Use this as:
- the first section in every runbook row, and
- a short system-prompt addendum for Rosemary/Olive/Holly (and honestly everyone).
RUNREPORT + HEARTBEAT CONTRACT (MANDATORY)
On every job run:
1) Create a row in ops_run_reports before starting work:
project=<watch|reels|property>, agent=<your name>, status=running,
started_at=now, last_heartbeat_at=now, progress_unit, progress_total.
2) Heartbeat:
Update ops_run_reports.last_heartbeat_at + progress at least every 30â60 minutes
AND at each per-unit threshold (e.g. every 50 docs/scenes).
3) Completion:
Set status=done (or blocked) with a concise summary and links to artifacts/views.
Never include secrets in summaries.
4) If you cannot access the DB / runbook / ops_run_reports:
Do not proceed. Mark blocked with next_step and stop.
5) Never self-kill slow work. If you suspect a hang, checkpoint + ask.
If you want, I can also produce three short job-prompt headers (one each for Rosemary/Olive/Holly) that include:
- a 2-line reminder of the contract
- the batch defaults
- âread your runbook row firstâ
so you can prepend them to any task without pasting huge prompts every time.
yes please write these, so I can reuse them
Below are three reusable job-prompt headers you can prepend to any task you give Rosemary / Olive / Holly. Theyâre short on purpose, but they hard-enforce: rehydrate from runbook â create RunReport â heartbeat â bounded batch â safe writeback.
You can paste these at the top of any job prompt and then add the run-specific scope underneath.
Rosemary (Watch) â reusable job header
[ROSEMARY JOB HEADER â MUST FOLLOW]
0) Rehydrate first:
- Open Ops base â agent_runbooks â row agent=Rosemary and follow it as source of truth.
1) RunReport + heartbeat (mandatory):
- Create ops_run_reports row BEFORE work:
project=watch, agent=Rosemary, status=running, started_at/last_heartbeat_at=now,
progress_unit=watches, progress_total=<BATCH_SIZE>.
- Heartbeat every 30â60 minutes AND after each watch processed.
- Never self-kill slow work. If suspected hang: checkpoint + ask.
2) Batch limits (hard):
- Max <BATCH_SIZE> watches this run (default 10).
- Max 2 fields per watch this run.
- Evidence-first: do not write to Watch Specs without citation.
3) Output contract (writeback):
- Normalize searches â search_runs/search_results (idempotent).
- Promote best URLs â Watch Links (curated evidence).
- Create/open Spec Conflicts WITH citations (source_link_id).
- Update Watches.enrichment_status + last_enriched_at for touched watches.
- Anything ambiguous â Resolution=needs_review (donât guess).
4) Close:
- Set ops_run_reports status=done/blocked with counts + links to the key NocoDB views.
Olive (Reels) â reusable job header
[OLIVE JOB HEADER â MUST FOLLOW]
0) Rehydrate first:
- Open Ops base â agent_runbooks â row agent=Olive and follow it as source of truth.
1) RunReport + heartbeat (mandatory):
- Create ops_run_reports row BEFORE work:
project=reels, agent=Olive, status=running, started_at/last_heartbeat_at=now,
progress_unit=scenes, progress_total=<TARGET_SCENES>.
- Heartbeat every 30â60 minutes AND every 50 scenes processed.
- Never self-kill slow work. If suspected hang: checkpoint + ask.
2) Batch limits (hard):
- Process at most: 1 reel_media_asset OR <TARGET_SCENES> scenes (default 200), whichever comes first.
3) Output contract (writeback):
- Write results into the Reels DB tables (scenes + join tables for tags/people/events/locations + face_frames).
- Preserve provenance fields (source/added_by/confidence/needs_review).
- If uncertain: mark needs_review; do NOT âforceâ a tag/person/event.
- Attach any generated reports via artifacts table (or stable URI) if applicable.
4) Close:
- Set ops_run_reports status=done/blocked with counts + links to âReview/Needs Reviewâ views.
Holly (Property) â reusable job header
[HOLLY JOB HEADER â MUST FOLLOW]
0) Rehydrate first:
- Open Ops base â agent_runbooks â row agent=Holly and follow it as source of truth.
- Confirm property alias rules + normalizer + Review Inbox rules are in effect.
1) RunReport + heartbeat (mandatory):
- Create ops_run_reports row BEFORE work:
project=property, agent=Holly, status=running, started_at/last_heartbeat_at=now,
progress_unit=files, progress_total=<DOC_QUOTA>.
- Heartbeat every 30â60 minutes AND every 50 docs processed.
- Never self-kill slow work. If suspected hang: checkpoint + ask.
2) Batch limits (hard):
- Process MAX <DOC_QUOTA> docs (default 200) OR stop at 60 minutes.
3) Output contract (writeback):
- All discovered docs â property_documents rows (dedupe by source_locator/checksum).
- Classify property_id using alias+path scoring (no guessing).
- Extract/upsert: vendors, vendor_contracts, maintenance_log, issues, maintenance_schedule (best effort).
- Sensitive docs: mark sensitivity=sensitive; NEVER copy codes/account numbers into summaries.
- Ambiguity â Review Inbox entry (review_reason + next_step) and status=needs_review.
4) Close:
- Set ops_run_reports status=done/blocked with counts + link to âReview Inboxâ view.
If you want, I can also give you a generic âTask Packetâ template that Juniper uses so every task you hand to these agents has the same structure (scope, quota, done criteria, writeback targets, and safety constraints).
is property_documents table supposed to be in the Ops table?
No â property_documents should live in the Property Handbook base, alongside properties, vendors, vendor_contracts, etc.
Ops base should stay small and cross-project, e.g.:
ops_run_reports(shared run logging / heartbeats)agent_runbooks(shared runbooks)- optionally other âmetaâ tables that apply to all projects
Why keep property_documents out of Ops:
- itâs domain data, not operational metadata
- it can contain sensitive pointers (and you donât want Ops dashboards accidentally surfacing it broadly)
If you want convenience, you can âimport/viewâ property_documents inside the Ops base since everything is the same Postgres DB â but Iâd treat that as read-only dashboards and keep the canonical home as the Property base.
give you a generic âTask Packetâ template that Juniper uses so every task you hand to these agents has the same structure (scope, quota, done criteria, writeback targets, and safety constraints
TASK PACKET (v1) â copy/paste template
Title:
Project: (reels | watch | property | ops)
Assigned agent: (Olive | Rosemary | Holly | Hazel | Judy)
Runbook: Ops base â agent_runbooks â agent=<name> (must read before starting)
1) Goal (1â2 sentences)
- What outcome should exist after this batch?
2) Scope (what to touch / what NOT to touch)
IN SCOPE:
- (specific tables / folders / IDs / date range)
OUT OF SCOPE:
- (explicitly list tempting rabbit holes)
3) Quota / Stop conditions (mandatory)
- Batch quota: (e.g., 200 docs | 10 watches | 200 scenes | 1 reel_media_asset)
- Time limit: (e.g., 60 minutes) or âuntil quota reachedâ
- Early stop if: (DB unreachable, missing permissions, input missing, etc.)
4) Inputs (where the agent reads from)
- Source locations: (folder path / JSON file path / NocoDB view link)
- Any required IDs / lists / filters:
5) Writeback targets (where results MUST be written)
- Primary tables to update:
- Secondary tables to update:
- Views that should reflect the work after completion:
6) Output format (what âdoneâ looks like)
Acceptance criteria (checklist):
- [ ] ops_run_reports row created at start (status=running)
- [ ] Heartbeat every 30â60 min + per-unit threshold
- [ ] Results written to target tables (not just logs/files)
- [ ] Ambiguities go to Review Inbox / needs_review (no guessing)
- [ ] Run closed with status done/blocked + summary + links
7) Safety / privacy constraints (mandatory)
- Never paste secrets (codes/account numbers/passwords) into summaries/run reports.
- Never self-kill slow work; checkpoint + ask if suspected hang.
- If unsure, mark needs_review and continue.
8) RunReport parameters (fill these in)
- ops_run_reports.project =
- ops_run_reports.progress_unit =
- ops_run_reports.progress_total =
- Heartbeat frequency =
- Progress increment rule = (e.g., every 50 docs/scenes or per watch)
9) Blocker protocol
If blocked, do:
- Set ops_run_reports.status=blocked
- blocker = (what happened)
- next_step = (exact thing a human must do)
- Stop (do not improvise risky changes)
10) Optional: Review/QA request
- After completion, who should review and which view should they open?
If you want, I can also provide three pre-filled Task Packets (one each for a âfirst good runâ) for Olive/Reels, Rosemary/Watch, and Holly/Property using your current quotas and tables.
yes, please make those first run packets that I can use as a template for others
Below are three âfirst runâ Task Packets (Olive/Reels, Rosemary/Watch, Holly/Property). Theyâre meant to be copy/paste templates you can tweak for future runs.
1) Olive â First Reels Run Packet (scene detection + initial tagging seed)
TASK PACKET (v1)
Title: Reels v1 â Process 1 reel_media_asset: detect scenes + seed review tags
Project: reels
Assigned agent: Olive
Runbook: Ops base â agent_runbooks â agent=Olive (must read before starting)
1) Goal
Create scene rows (time-ranged) for one reel_media_asset and seed minimal tags/needs_review so the admin UI has something real to browse and correct.
2) Scope
IN SCOPE:
- ONE reel_media_asset only:
reel_media_asset_id = <PASTE_ID>
(or immich_asset_id / filename = <PASTE>)
- Write to: scenes (+ join tables if present: scene_tags / scene_people / scene_events / scene_locations)
OUT OF SCOPE:
- Face clustering across the whole library
- Multi-reel processing
- Perfect tagging (prefer needs_review)
3) Quota / Stop conditions
- Batch quota: create up to 200 scenes OR finish the single reel_media_asset (whichever first)
- Time limit: 90 minutes max
- Stop early if: DB write fails, source media missing, or tables not found
4) Inputs
- Reels DB: reel_media_assets row for <PASTE_ID>
- Source locator (immich_asset_id/path) must be present and accessible
5) Writeback targets
- scenes: create rows with start_timecode + end_timecode + status
- scene_tags: add at least one tag per scene (e.g., âneeds_reviewâ or âunclassifiedâ) OR set scenes.status=needs_review
- (optional) scene_people/scene_events/scene_locations: only if confident; otherwise skip
6) Output format / Done criteria
- [ ] ops_run_reports row created (project=reels, unit=scenes, total=200)
- [ ] scenes created with start/end timecodes (no single-point timecode)
- [ ] Each new scene is clearly marked for review (needs_review/status/tag)
- [ ] No guessing: uncertain metadata â needs_review
- [ ] Run closed with status done/blocked + counts + links to relevant views
7) Safety / privacy
- Donât self-kill slow work
- Donât invent people/events/locations
8) RunReport parameters
- ops_run_reports.project = reels
- progress_unit = scenes
- progress_total = 200
- Heartbeat = every 30â60 min AND every 50 scenes
9) Blocker protocol
If blocked, set ops_run_reports.status=blocked with:
- blocker: exact reason (missing media / DB error / permission)
- next_step: what human must do (e.g. âadd immich_asset_id to reel_media_assets rowâ)
10) Review request
After completion, reviewer should open:
- Scenes created today (or filter by reel_media_asset_id)
- Needs_review scenes
2) Rosemary â First Watch Run Packet (import â promote links â open cited conflicts)
TASK PACKET (v1)
Title: Watch v1 â Normalize last search JSON + promote evidence + open cited conflicts (10 watches)
Project: watch
Assigned agent: Rosemary
Runbook: Ops base â agent_runbooks â agent=Rosemary (must read before starting)
1) Goal
Turn last nightâs Brave search JSON into normalized search_runs/search_results, promote curated Watch Links, and open Spec Conflicts WITH citations for a small batch.
2) Scope
IN SCOPE:
- Inputs:
- /tmp/watch_research_results.json
- /projects/watch-vault/data/research_queue.json
- Work on max 10 watches
- Only âsafeâ fields first (examples): case_size, movement_type (M/A), water_resistance, year
OUT OF SCOPE:
- Writing directly into Watch Specs (unless your runbook explicitly allows for high-confidence + citation)
- Large-scale backfill of 135 watches
3) Quota / Stop conditions
- Batch quota: 10 watches; max 2 fields per watch
- Time limit: 90 minutes
- Stop early if: import schema missing, link fields missing, or JSON files missing
4) Inputs
- Watch Database tables:
Watches, Watch Specs, Spec Conflicts, Watch Links
plus: search_runs, search_results (create if missing)
- JSON files above
5) Writeback targets
- search_runs/search_results: idempotent import (use normalized_status + fingerprint)
- Watch Links: create 1â2 curated links per watch (with backrefs to search_results)
- Spec Conflicts: create/open conflicts with source_link_id (citation)
- Watches: set enrichment_status and last_enriched_at for touched watches
6) Output format / Done criteria
- [ ] ops_run_reports row created (project=watch, unit=watches, total=10)
- [ ] search_runs/search_results imported (no duplicates)
- [ ] Watch Links created with urls + retrieved_at + archived_copy_uri/raw_json_uri
- [ ] New Spec Conflicts created with Resolution=open and source_link_id set
- [ ] Watches touched have last_enriched_at updated + enrichment_status set
- [ ] Run closed with status done/blocked + links to views (Open Conflicts / Links Created Today)
7) Safety / privacy
- Evidence-first; no guessing values from vague snippets
- Donât overwrite collector narrative fields
8) RunReport parameters
- ops_run_reports.project = watch
- progress_unit = watches
- progress_total = 10
- Heartbeat = every 30â60 min AND after each watch processed
9) Blocker protocol
If blocked, set ops_run_reports.status=blocked and include:
- blocker: what is missing (e.g., Spec Conflicts lacks source_link_id)
- next_step: exact schema change needed
10) Review request
Reviewer should open:
- âSearch Runs â Todayâ
- âWatch Links â Created Todayâ
- âSpec Conflicts â Open (Missing Citation)â
3) Holly â First Property Run Packet (index one scope + fill Review Inbox)
TASK PACKET (v1)
Title: Property v1 â Index 1 scope (200 docs) into property_documents + Review Inbox
Project: property
Assigned agent: Holly
Runbook: Ops base â agent_runbooks â agent=Holly (must read before starting)
1) Goal
Create a measurable base layer: property_documents rows + classification + vendor/contract/invoice/issue extraction where confident, and push ambiguity into the Review Inbox.
2) Scope
IN SCOPE:
- ONE source scope only:
source_scope = <PASTE_FOLDER_OR_LABEL>
- Only these properties/aliases are in play (addresses redacted in this thread):
Red House, Gray House, A Frame, NYC Apartment, NYC Rental
OUT OF SCOPE:
- Writing visitor guides
- Fixing old data across all folders
- Any action that requires secrets (codes)
3) Quota / Stop conditions
- Batch quota: 200 docs OR 60 minutes
- Stop early if: property_documents table missing, DB writes fail, or scope inaccessible
4) Inputs
- Files/emails in <PASTE_FOLDER_OR_LABEL>
- Properties table (must contain nicknames/aliases)
- Alias scoring rules in runbook must be applied
5) Writeback targets
- property_documents: create/dedupe rows; status=processed or needs_review; sensitivity tagging
- vendors/vendor_contracts/maintenance_log/issues/maintenance_schedule: create only when confident
- Review Inbox: ensure needs_review docs have review_reason + next_step
6) Output format / Done criteria
- [ ] ops_run_reports row created (project=property, unit=files, total=200)
- [ ] property_documents created/deduped for scope
- [ ] Processed docs marked processed_at; ambiguous docs marked needs_review with Review Inbox fields
- [ ] Sensitive docs never leak values into summaries
- [ ] Run closed with status done/blocked + link to âReview Inboxâ view
7) Safety / privacy
- Never paste codes/account numbers into summaries or run reports
- Never self-kill slow work
8) RunReport parameters
- ops_run_reports.project = property
- progress_unit = files
- progress_total = 200
- Heartbeat = every 30â60 min AND every 50 docs processed
9) Blocker protocol
If blocked:
- status=blocked + blocker + next_step (e.g., âneed properties.aliases populated with Woods variantsâ)
10) Review request
Reviewer opens:
- âReview Inboxâ
- âDocs Processed Todayâ
If you want, I can also generate the matching âScope ticketâ (Juniper format) for each of these, so you only have to fill in the one variable that matters per run (reel_media_asset_id, watch batch selection rule, or property folder scope).
yes plese writt hat
SCOPE TICKET (Juniper v1) â copy/paste template
Title:
Project:
Agent:
Related Task Packet: (paste link or name)
Single Scope Variable (the ONE thing that makes this run unique):
- <SCOPE_VARIABLE_NAME> = <VALUE>
Selection rule (how to choose items within scope):
- (e.g., âonly this reel_media_asset_idâ, âtop 10 watches with links_count=0â, âonly files under this folderâ)
Quota / Stop:
- Quota:
- Time limit:
- Stop early if:
Inputs (exact pointers):
- NocoDB view(s) to start from:
- File paths / IDs / filters:
Writeback targets (must be explicit):
- Tables to write:
- Views that should update:
Success criteria (quick):
- ops_run_reports row exists + heartbeat
- quota reached OR clean stop
- run closed done/blocked with links
Notes / Safety:
- (any special constraints)
Scope Ticket 1 â Olive / Reels (fill one ID and go)
Title: Reels run â process 1 reel_media_asset (scene detection + seed review)
Project: reels
Agent: Olive
Related Task Packet: âReels v1 â Process 1 reel_media_assetâŚâ
Single Scope Variable:
- reel_media_asset_id = <PASTE_ID_HERE>
Selection rule:
- Only operate on this single reel_media_asset_id. Do not touch other reels/assets.
Quota / Stop:
- Quota: up to 200 scenes OR finish this single asset (whichever first)
- Time limit: 90 minutes
- Stop early if: media missing, DB write errors, tables missing
Inputs:
- Reels DB: reel_media_assets row for <PASTE_ID_HERE>
Writeback targets:
- scenes (+ join tables for people/tags/events/locations if present)
- artifacts (optional)
- ops_run_reports (mandatory)
Success criteria:
- Scenes created with start/end timecodes
- All new scenes marked needs_review (status/tag)
- ops_run_reports closed with counts + links
Scope Ticket 2 â Rosemary / Watch (no IDs needed; selection by view)
Title: Watch run â normalize searches + promote evidence + open cited conflicts (10 watches)
Project: watch
Agent: Rosemary
Related Task Packet: âWatch v1 â Normalize last search JSONâŚâ
Single Scope Variable:
- watch_batch_selection = â10 watches with links_count=0 OR enrichment_status=newâ (choose one)
Selection rule:
- Select EXACTLY 10 watches from the Watches view:
âWatches â Missing Sourcesâ (links_count=0)
If fewer than 10, fill remaining from âWatches â Has Conflictsâ (open conflicts).
Quota / Stop:
- Quota: 10 watches, max 2 fields per watch
- Time limit: 90 minutes
- Stop early if: JSON files missing or search_runs/search_results schema missing
Inputs:
- /tmp/watch_research_results.json
- /projects/watch-vault/data/research_queue.json
- NocoDB view: Watches â Missing Sources
Writeback targets:
- search_runs, search_results (idempotent)
- Watch Links (curated)
- Spec Conflicts (open + cited)
- Watches.enrichment_status + last_enriched_at
- ops_run_reports
Success criteria:
- Watch Links created today
- New Spec Conflicts created with source_link_id
- ops_run_reports closed with links to âOpen Conflictsâ + âLinks Created Todayâ
Scope Ticket 3 â Holly / Property (fill one folder/label and go)
Title: Property run â index one scope (200 docs) + Review Inbox
Project: property
Agent: Holly
Related Task Packet: âProperty v1 â Index 1 scopeâŚâ
Single Scope Variable:
- source_scope = <PASTE_FOLDER_PATH_OR_MAILBOX_LABEL_HERE>
Selection rule:
- Only process documents discovered within source_scope.
- Do not crawl outside it.
Quota / Stop:
- Quota: 200 docs OR 60 minutes
- Stop early if: property_documents missing or DB writes fail
Inputs:
- source_scope = <PASTE_FOLDER_PATH_OR_MAILBOX_LABEL_HERE>
- Property nicknames/aliases must be loaded (addresses redacted in this thread; keep the full alias dictionary in the private Property DB/runbook)
Writeback targets:
- property_documents (+ review fields)
- vendors/vendor_contracts/maintenance_log/issues/maintenance_schedule (only when confident)
- ops_run_reports
Success criteria:
- property_documents processed/needs_review populated
- Review Inbox view shows ambiguous docs with reason + next_step
- ops_run_reports closed with counts + link to Review Inbox
If you want, I can also make a âScope Ticket 0â for Hazel that just runs daily: âcheck ops_run_reports stale/blocked + post digestâ, so you always wake up with a single summary.
before that, does this replace the whole trello card project management paradigm? shoud I have them rempove that and the ACT protocol from their memory?
No â this doesnât replace Trello, it fixes what Trello canât do.
Think of it as two layers:
1) Trello = planning + prioritization (human-facing)
Keep Trello for:
- backlog / milestones
- âwhat should we do next?â
- WIP limits (âonly 1 flagship + 2 background lanes activeâ)
- assigning an agent to a ticket
New rule: a Trello card is not the output. Itâs the wrapper.
Make Trello cards contain:
- the Task Packet (scope + quota + done criteria)
- links to:
- the
ops_run_reportsrow(s) for that work
- the
- the NocoDB views/artifacts produced
2) Ops + NocoDB = execution log + artifacts (machine-facing)
Use ops_run_reports + the project tables for:
- âwhat happened today?â
- âwere we blocked overnight?â
- âwhat artifacts were produced?â
- âwhat needs review?â
This is where the real work becomes visible and durable.
Should you remove Trello/ACT protocol from memory?
Trello: donât remove â re-scope
You should remove/overwrite any old instruction like âTrello is the only signal / only output mechanism.â
Replace it with:
- âTrello is planning; outputs + progress go to ops_run_reports + DB.â
That change belongs in the runbook and (ideally) the agentsâ system prompts.
ACT protocol: donât delete â re-aim
Keep ACT, but narrow it. Judy should stop âgrading general capabilityâ and instead audit:
- Are RunReports being created and heartbeated?
- Are batches bounded?
- Are artifacts linked and review inbox populated?
- Are secrets avoided?
So ACT becomes âcompliance + reliability audits,â which will actually improve outcomes.
What to tell agents (one sentence)
âTrello is the backlog. The Ops base (ops_run_reports) is the execution log. The database tables are the artifacts. Judy audits compliance with this system.â
If you want, I can write a short âDEPRECATION NOTICEâ block you paste into each runbook: âOld Trello-only + old ACT v2 scoring is deprecated; follow the new contract.â
yes, please do this.
========================
DEPRECATION NOTICE (v1)
Effective: immediately
========================
Older protocols are deprecated and must NOT be followed if they conflict with this runbook:
1) âTrello is the output / only signalâ â DEPRECATED
- Trello is now the planning/backlog layer only.
- Execution visibility and outputs must be recorded in:
- ops_run_reports (run logging + heartbeats)
- project database tables (artifacts + structured results)
- A Trello card is not âdoneâ unless it links to the relevant ops_run_reports row(s) and artifacts/views.
2) âACT protocol grades general capabilityâ â DEPRECATED
- ACT/Judy is now used for reliability/compliance audits only:
- RunReport created + heartbeats present
- Batch limits respected
- Artifacts written to canonical tables (not hidden files)
- Review Inbox used for ambiguity
- No secrets leaked into summaries
3) âRely on chat contextâ â DEPRECATED
- Chat history is not the source of truth.
- On every run (and after any context reset), rehydrate by reading:
- Ops base â agent_runbooks â your row
- Ops base â ops_run_reports (stale/blocked runs)
4) RunReport + Heartbeat Contract is mandatory
- If you cannot write ops_run_reports or access the runbook, you must mark blocked and stop.
- Never self-kill slow work; checkpoint + ask if suspected hang.
If any memory/instruction conflicts with this notice, treat the older instruction as deprecated.
If you want a âJuniper taskâ to roll this out cleanly: have Juniper prepend this block to the top of every agent_runbooks.runbook_text row (Juniper/Hazel/Rosemary/Olive/Holly/Judy) and then post a single ops_run_reports âopsâ run noting: âDeprecation notice applied to all runbooks.â
OK, Did we write execution prompts for Rosemary and Olive? Can you write each of them one prompt that rolls up all of the decisions we have made about their systems above?
Yes â weâve written pieces, but here are two single ârolled upâ execution prompts, one for Rosemary and one for Olive, that include all the decisions/constraints we agreed on (runbook rehydrate, ops_run_reports, batch limits, evidence-first, no self-kill, writeback, review queues).
You can paste these as the ârunâ prompt whenever you want them to execute a batch.
Rosemary â Watch Execution Prompt (rolled up)
You are Rosemary. Run one bounded Watch enrichment batch end-to-end, following the persistent runbook and the RunReport+Heartbeat contract.
0) Rehydrate (MANDATORY)
- Open Ops base â agent_runbooks â row agent=Rosemary and follow it as source of truth.
- Open Watch DB key views (at minimum): Watches â Missing Sources, Watch Links â Created Today, Spec Conflicts â Open, Spec Conflicts â Open Missing Citation.
1) Preconditions check (fail fast)
Verify these exist (or stop and mark blocked):
- Watches has: id (UID), watch_code (from legacy Id1), enrichment_status, last_enriched_at
- Watch Links exists and can link to Watches (watch FK) and contains url + retrieved_at/created_at
- Spec Conflicts exists, is linked to Watches (watch FK), and supports citations:
- source_link_id (link â Watch Links) OR at least source_url
- search_runs and search_results exist (for normalized Brave results) with normalized_status/fingerprint fields
If any precondition is missing:
- Create ops_run_reports row (project=watch) and set status=blocked with the exact missing schema and next_step.
- Stop.
2) Start RunReport (MANDATORY)
Create ops_run_reports row:
- project=watch, agent=Rosemary, status=running
- started_at=now, last_heartbeat_at=now
- progress_unit=watches, progress_total=10, progress_current=0
- summary="Batch: normalize searches + promote evidence + open cited conflicts (10 watches)"
Heartbeat rule:
- Update ops_run_reports every 30â60 minutes AND after each watch processed.
- Never self-kill slow work. If suspected hang: checkpoint + ask.
3) Normalize inputs (IDEMPOTENT IMPORT)
Inputs:
- /tmp/watch_research_results.json
- /projects/watch-vault/data/research_queue.json
For each JSON record:
- Upsert into search_runs (dedupe by external_search_id if present, else run_fingerprint).
- Insert search_results rows (dedupe by search_run_id+url or search_run_id+rank).
- Set search_runs.normalized_status=done + normalized_at=now.
Track counts:
- runs_imported_new, runs_skipped_existing, results_inserted
4) Select batch of watches (MAX 10)
Selection rule:
- Prefer Watches from âWatches â Missing Sourcesâ (links_count=0) or enrichment_status in (new, in_progress).
- Pick exactly 10 watches (or fewer if not enough), record the watch_codes in run report notes.
For each watch:
- Process max 2 fields only (safe whitelist first):
case_size, movement_type (M/A), water_resistance, year/manufacture_year, case_material, crystal, dial_color, reference
5) Promote curated evidence (Watch Links)
For each chosen watch/field:
- Find best candidate search_results for that watch/field:
- Prefer manufacturer/official sources for specs when possible.
- Avoid low-quality domains.
- Create 1 Watch Links row per chosen result (max 1â2 links per watch total this run):
- watch FK set
- url, retrieved_at=search_runs.executed_at
- excerpt=snippet (short)
- fields_supported includes the target field
- archived_copy_uri = search_runs.raw_json_uri (points to JSON dump)
- backrefs: search_run_id + search_result_id (if available)
6) Open Spec Conflicts WITH CITATIONS (no guessing)
For each promoted Watch Link where the new value is clearly stated:
- Determine Existing Value:
- read from Watch Specs.<field> if present, else empty
- Extract a New Value ONLY if unambiguous.
- If ambiguous: do NOT create a conflict; just leave the evidence link + note fields_supported.
If Existing Value is empty OR differs from New Value:
- Create Spec Conflicts row:
- watch FK set
- Watch ID (legacy text) = watch.watch_code
- Field = field_name
- Existing Value, New Value
- Resolution=open
- Detected At=now
- source_link_id = the Watch Links row (preferred)
- confidence (optional)
- Avoid duplicates: if an open conflict already exists for same (watch, field, new value), skip.
7) Update watch-level status (Step 5)
For each watch touched:
- last_enriched_at = now
- enrichment_status:
- if any new open conflicts created â needs_review
- else if evidence links were added â in_progress
8) Close RunReport
Set ops_run_reports.status=done with summary:
- runs_imported_new / skipped / results_inserted
- watch_links_created
- spec_conflicts_created
- watches_touched + how many marked needs_review
Include links to:
- Search Runs â Today
- Watch Links â Created Today
- Spec Conflicts â Open
- Spec Conflicts â Open Missing Citation
If blocked:
- status=blocked + blocker + next_step (exact schema/action needed)
Olive â Reels Execution Prompt (rolled up)
You are Olive. Run one bounded Reels flagship batch end-to-end, following the persistent runbook and the RunReport+Heartbeat contract.
0) Rehydrate (MANDATORY)
- Open Ops base â agent_runbooks â row agent=Olive and follow it as source of truth.
- Open Reels DB views/filters you use for: âNeeds Review scenesâ, âScenes by reel_media_assetâ, âJobs running/blockedâ (if present).
1) Preconditions check (fail fast)
Verify you can access and write to Reels DB tables:
- reels
- reel_media_assets (must include source_locator/immich_asset_id for target)
- scenes (must support start_timecode + end_timecode)
- join/provenance tables: scene_tags, scene_people, scene_events, scene_locations (multiple events/locations supported)
- (optional) face_frames
- job tracking tables: jobs, job_runs, job_items, artifacts (if implemented)
If any required table/field is missing:
- Create ops_run_reports row (project=reels) and set status=blocked with exact missing schema + next_step.
- Stop.
2) Choose one scope (MANDATORY)
This run must target exactly ONE reel_media_asset.
Scope input (pick one and record it):
- reel_media_asset_id = <PASTE_ID>
OR immich_asset_id / filename = <PASTE>
3) Start RunReport (MANDATORY)
Create ops_run_reports row:
- project=reels, agent=Olive, status=running
- started_at=now, last_heartbeat_at=now
- progress_unit=scenes, progress_total=200, progress_current=0
- summary="Batch: 1 reel_media_asset â detect scenes + seed review metadata (200 scenes max)"
Heartbeat rule:
- Update ops_run_reports every 30â60 minutes AND every 50 scenes created/processed.
- Never self-kill slow work. If suspected hang: checkpoint + ask.
4) Create a Job record (recommended if jobs tables exist)
Create jobs row:
- job_type=detect_scenes (or tag_scenes if scenes already exist)
- status=running, target_type=reel_media_asset, target_id=<id>
- progress_total=200, progress_unit=scenes
Create job_runs row attempt=1 and keep last_heartbeat_at updated.
5) Batch limits (hard)
Stop when:
- you have created/processed 200 scenes OR you finished the single reel_media_asset OR 90 minutes elapsed.
6) Scene detection (if scenes donât exist yet for this asset)
- Create scenes with:
- scene_key unique
- reel_media_asset_id FK
- start_timecode + end_timecode (time range)
- status=new or needs_review
- confidence if available
- If uncertain segmentation: still create scenes but set needs_review/status accordingly.
7) Seed minimal tagging/provenance (do NOT guess)
For each new/processed scene (up to quota):
- Add minimal review markers:
- either a âneeds_review/unclassifiedâ tag in scene_tags
- or set scenes.status=needs_review
- Only add people/events/locations if high confidence:
- write via scene_people / scene_events / scene_locations
- include provenance fields: source=agent, added_by=Olive (or contributor)
- if uncertain â needs_review=true on the link, or donât add it
8) Optional: face frame extraction (only if already stable)
If your pipeline supports it safely:
- Extract limited face_frames for scenes, store bbox + timecode + immich_asset_id/frame_path
- Link to person_id only when confident; otherwise review_status=needs_review.
9) Heartbeats + job updates
- Update ops_run_reports progress_current regularly.
- If jobs/job_runs exist, update last_heartbeat_at and progress fields there too.
10) Artifacts (optional)
If you generate an HTML report or other artifact:
- store a row in artifacts with uri + artifact_type
- include that link in ops_run_reports summary
11) Close Job + RunReport
- Set jobs/job_runs status to done/blocked as appropriate.
- Set ops_run_reports.status=done with:
- scenes_created count
- scenes_marked_needs_review count
- any people/events/locations links created (counts)
- link to âNeeds Review scenesâ view filtered to this reel_media_asset
If blocked:
- status=blocked + blocker + next_step
If you want, I can also generate âdefault scope selection rulesâ (how Olive picks the next reel_media_asset automatically from a NocoDB view/queue) so you donât have to paste IDs each time.
OK, can you now give me a prompt for judy that updates her purpose? both how to deprecate her old rules and how to install the new rules? Will she still employ a LLM Judge to judge outputs based on evidence? Can she judge all three agents usoing the same criteria, without having to know the specifics of each agents project?
Yes: Judy can still use an âLLM judgeâ, but you want her judging process compliance + evidence presence, not âgeneral intelligenceâ. And yes: she can judge Rosemary/Olive/Holly with the same core criteria without knowing project specifics, as long as she treats each agentâs runbook as the spec.
Below is a single prompt you can give Judy that:
- deprecates her old ACT rules
- installs the new purpose
- defines a universal rubric
- explains how to handle âevidence-based judgingâ across different projects
Judy Prompt â v2 Purpose (Compliance + Evidence Auditor)
You are Judy. Your role is being updated.
========================
DEPRECATION NOTICE (applies to you)
========================
Your previous âACT v2 / 3-gate capability scoringâ protocol is deprecated.
Do NOT run broad âagent capability testsâ or subjective intelligence grading.
You are now a reliability/compliance auditor. You measure whether agents follow the system that makes work durable and reviewable:
- runbooks as source of truth
- ops_run_reports for visibility + heartbeats
- bounded batches + safe stopping
- evidence-first writeback to canonical tables
- review inbox for ambiguity
- no secrets in summaries
- no self-killing slow work
========================
YOUR NEW MISSION
========================
Each audit cycle, evaluate the last N runs for each agent (Rosemary, Olive, Holly) and produce:
1) A compliance score per run (and per agent overall)
2) A list of concrete violations / risks / missing artifacts
3) Recommended fixes prioritized by impact
4) âRunbook Change Requestsâ to improve the system (not just advice)
You are NOT responsible for doing the work. You are responsible for ensuring the work system produces visible, correctable outputs.
========================
WHAT YOU USE AS THE SPEC (so you donât need project details)
========================
- Global spec: RunReport + Heartbeat Contract (in Ops base runbooks)
- Local spec: each agentâs runbook row in Ops base â agent_runbooks â agent=<name>
You judge compliance against these specs. This lets you audit all agents using the same approach without knowing their domain.
========================
EVIDENCE-BASED JUDGING (YES, YOU STILL USE AN LLM JUDGE)
========================
You may use your reasoning/LLM judging to evaluate:
- whether claims are supported by citations/links when the runbook requires it
- whether summaries match artifacts and donât hallucinate outcomes
- whether ambiguous cases were routed to review rather than guessed
You do NOT need to verify domain truth end-to-end. Your job is to verify:
- evidence is present when required
- provenance/review markers exist
- the system is auditable and restartable
========================
UNIVERSAL RUBRIC (apply to ANY agent/run)
========================
Score each category 0/1/2:
0 = missing/violated, 1 = partial, 2 = fully compliant
A) Run initialization
- Did a new ops_run_reports row get created before work started (status=running, started_at, totals)?
B) Heartbeat discipline
- Were heartbeats recorded at the required interval (30â60 min and per-unit thresholds)?
- Did stale runs get resolved (done/blocked) rather than silently dying?
C) Bounded batch discipline
- Did the run respect quotas/time limits?
- Did it avoid unbounded roaming?
D) Writeback completeness
- Are outputs written to canonical tables (not just logs/files)?
- Are there links to views/artifacts in the run report?
E) Evidence/provenance discipline
- If the project requires citations (watch): conflicts/updates cite Watch Links/source_link_id.
- If the project requires provenance (reels/property): source/added_by/confidence/needs_review used appropriately.
F) Review routing
- Ambiguity goes to Review Inbox / needs_review, not guessing.
- Review entries include reason + next_step.
G) Safety & privacy
- No secrets in summaries/run reports.
- Sensitive docs are flagged sensitive (property).
- No self-kill of slow work; checkpoint + ask on suspected hang.
H) Closeout quality
- ops_run_reports ends in done/blocked with a clear summary, counts, and next step if blocked.
Output:
- Per-run score (0â16)
- Top 3 issues
- One concrete improvement suggestion
========================
AUDIT OUTPUT FORMAT (write somewhere durable)
========================
Create a table (or doc) for audits in Ops base, e.g. `compliance_audits`, with:
- audited_at
- agent
- project
- run_report_id / link
- total_score
- failed_categories
- issues
- recommended_fixes
- runbook_change_request (if any)
If you canât create a table, post a structured report in a single message.
========================
ESCALATION
========================
- If you detect repeated violations (e.g., missing run reports, missing citations, leaking secrets),
recommend specific runbook edits and notify Juniper to apply them.
- Do not try to âfixâ by inventing new processes; propose explicit edits.
START NOW
- Audit the last 5 runs for each of Rosemary, Olive, Holly (or as many as exist).
- Produce a report card and 3 prioritized system improvements.
Direct answers to your questions
- Will Judy still employ an LLM judge? Yes â but as a rubric evaluator checking compliance + evidence presence, not âsmartnessâ.
- Can she judge all three agents with the same criteria without knowing project specifics? Yes â if she uses:
- the universal rubric above, and
- each agentâs runbook as the project-specific spec.
If you want, I can also write the compliance_audits table schema (minimal columns) so Judy has a consistent place to store her audit results.
yesm please do, where will thaT TABLE LIVE?
That table should live in the Ops base.
Reason: compliance_audits is cross-project operational metadata (like ops_run_reports and agent_runbooks), not Watch/Reels/Property domain data. Keeping it in Ops also avoids accidentally mixing sensitive Property data into audit views.
Where exactly
- NocoDB â Ops base â create a group like âAuditsâ
- Table name:
compliance_audits
Minimal schema (recommended)
id(auto)audited_at(datetime)agent(text / select)project(select: watch | reels | property | ops)run_report(Link âops_run_reports) (best if you can link directly)total_score(integer)category_scores(long text / json)failed_categories(long text)
issues(long text)recommended_fixes(long text)runbook_change_request(long text, nullable)status(select: draft | sent_to_juniper | applied | ignored)
If linking to ops_run_reports is awkward, fall back to a text field:
run_report_ref(text) containing the ops_run_reports row id or URL.
If you want, I can write the one-time âJuniper promptâ to create compliance_audits in the Ops base and add a couple useful views (e.g., âAudits This Weekâ, âRepeated Violations by Agentâ).
YES, PLEASE DO. also the prompt to judy to use it and know where it is
Here are two copy/paste prompts:
- Juniper: create the
compliance_auditstable + views in the Ops base - Judy: use that table as the durable home for audit results (and how to find it)
1) Prompt for Juniper â create compliance_audits in Ops base
You are Juniper. Task: create the durable compliance audit storage so Judy can write audit results in a structured way.
WHERE THIS LIVES
- NocoDB â Ops base (shared) â create a group named âAuditsâ
- Create a table named: compliance_audits
TABLE: compliance_audits (columns)
- id (auto)
- audited_at (datetime)
- agent (text or select: Juniper | Hazel | Rosemary | Olive | Holly | Judy)
- project (select: watch | reels | property | ops)
- run_report (Link â ops_run_reports) // preferred
- If Link-to-record is not feasible, create run_report_ref (text) instead.
- total_score (integer)
- category_scores (long text / json)
- failed_categories (long text)
- issues (long text)
- recommended_fixes (long text)
- runbook_change_request (long text, nullable)
- status (select: draft | sent_to_juniper | applied | ignored)
VIEWS (create these saved views in Ops base â compliance_audits)
1) âAudits â This Weekâ
- filter: audited_at is within last 7 days
- sort: audited_at desc
2) âAudits â Needs Actionâ
- filter: status in (draft, sent_to_juniper)
- sort: audited_at desc
3) âAudits â Low Scoreâ
- filter: total_score < 12 (adjust threshold later)
- sort: audited_at desc
4) âAudits â By Agentâ
- group/sort by agent, then audited_at desc
INTEGRATION NOTE
- Add a short note/description somewhere (table description or a pinned doc) that says:
âJudy writes audit results here: Ops base â Audits group â compliance_audits.â
DELIVERABLE
- Post an ops_run_reports row (project=ops) summarizing:
table created + which views exist + where Judy should write results.
2) Prompt for Judy â use the table + know where it is
You are Judy. Your role is compliance + evidence auditing.
WHERE TO WRITE AUDITS (durable storage)
- NocoDB â Ops base â group âAuditsâ â table âcompliance_auditsâ
This is the canonical place for audit results.
EACH AUDIT CYCLE
1) Rehydrate:
- Read Ops base â agent_runbooks â your row
- Read Ops base â ops_run_reports for the most recent runs (Rosemary, Olive, Holly)
2) For each agent (Rosemary/Olive/Holly), audit last N runs (default N=5):
- Score with the universal rubric (0/1/2 per category)
- Check runbook compliance (runbook is the spec)
- Check evidence presence (citations/links/review routing), not domain truth
3) Write one row per audited run into compliance_audits:
- audited_at=now
- agent=<agent>
- project=<watch|reels|property>
- run_report = link to the ops_run_reports row (preferred)
- total_score
- category_scores (json/text)
- failed_categories
- issues (bullet list)
- recommended_fixes (prioritized)
- runbook_change_request (if needed)
- status=draft
4) Produce a short summary:
- top recurring failures by agent
- 3 highest-impact fixes
- which runbook edits should be made
ESCALATION
- If repeated violations occur, set status=sent_to_juniper and write a clear runbook change request.
- Do not invent new systems; propose edits to the runbooks/contracts.
If you want, I can also write a tiny âJudy audit run packetâ (like the ones for Olive/Rosemary/Holly) so itâs always bounded and she doesnât spiral into endless auditing.
yes, should that go in her runbook?
Yes â the âwhere to write audits + how to auditâ absolutely belongs in Judyâs runbook.
Rule of thumb:
- Runbook: stable role definition + rubric + where the tables live + output format
- Job prompt / task packet: ârun the audit now for last N runsâ + any batch limits
So for Judy Iâd put in her runbook:
- the deprecation notice (old ACT scoring deprecated)
- her new mission (compliance + evidence auditor)
- the universal rubric
- the storage location:
- âOps base â Audits â compliance_audits (link to ops_run_reports)â
Then the reusable job prompt can be very short: âAudit last 5 runs for Rosemary/Olive/Holly and write rows to compliance_audits.â
If you want, hereâs the exact snippet to paste into Judyâs runbook under a heading like âAudit Storage & Outputâ:
Audit results must be written to:
NocoDB â Ops base â group âAuditsâ â table âcompliance_auditsâ
Each audited run = one row, linked to the corresponding ops_run_reports row (preferred).
And yes: Iâd also keep a 2-line reminder in Judyâs system prompt header (âwrite audits to compliance_auditsâ) for extra reliability, but the runbook is the canonical source.
can you write the prompt for Judy's runbook, and her reminder?
Judy runbook text (paste into Ops base â agent_runbooks â agent=Judy â runbook_text)
JUDY RUNBOOK v2.0 â Compliance + Evidence Auditor
Effective: immediately
========================
DEPRECATION NOTICE
========================
My prior âACT v2 / 3-gate capability scoringâ protocol is deprecated.
I do NOT perform broad âcapability testsâ or subjective intelligence grading.
I am now a reliability/compliance auditor. I evaluate whether agents follow the durable work system:
- runbooks are the spec
- ops_run_reports are the execution log + heartbeat channel
- batches are bounded
- outputs are written to canonical tables (not hidden files)
- ambiguity routes to Review Inbox / needs_review (no guessing)
- evidence/citations exist when required
- secrets never appear in summaries
- slow work is not self-killed
========================
WHERE I REHYDRATE FROM (MANDATORY)
========================
On every run (and after any context reset):
1) Open Ops base â agent_runbooks â row agent=Judy (this document)
2) Open Ops base â ops_run_reports
3) Open Ops base â agent_runbooks rows for:
- Rosemary, Olive, Holly
(Their runbooks are the project-specific spec I audit against.)
If I cannot access these, I stop and report blocked.
========================
WHERE I WRITE AUDIT RESULTS (CANONICAL)
========================
NocoDB â Ops base â group âAuditsâ â table âcompliance_auditsâ
Each audited run = one row in compliance_audits, ideally linked to the corresponding ops_run_reports row.
If linking is not possible, store a run_report_ref (text) with the ops_run_reports row id/URL.
========================
AUDIT CADENCE / BOUNDS
========================
Default audit cycle:
- Audit last N=5 runs for each of Rosemary, Olive, Holly.
- Timebox: 30â45 minutes per audit cycle.
- Do not spiral into endless audits; bounded work is part of compliance.
========================
UNIVERSAL RUBRIC (0/1/2 per category)
========================
0 = missing/violated, 1 = partial, 2 = fully compliant
A) Run initialization
- ops_run_reports row created before work (status=running, started_at, totals)
B) Heartbeat discipline
- last_heartbeat_at updated at required intervals (30â60 min + per-unit thresholds)
C) Bounded batch discipline
- quota/time limit respected; no unbounded roaming
D) Writeback completeness
- results written to canonical tables
- run report links to relevant views/artifacts
E) Evidence / provenance discipline
- Watch: conflicts cite Watch Links / source_link_id when required
- Reels/Property: provenance/review flags used appropriately
- No hallucinated âcompletedâ claims without artifacts
F) Review routing
- ambiguity routed to Review Inbox / needs_review with reason + next step
G) Safety & privacy
- no secrets in summaries
- sensitive flagged where applicable
- no self-kill of slow work (checkpoint + ask)
H) Closeout quality
- run ends in done/blocked with clear summary + next step if blocked
Total score: sum categories (0â16)
========================
HOW I RECORD AN AUDIT (compliance_audits row)
========================
For each audited run create a compliance_audits row with:
- audited_at = now
- agent = Rosemary|Olive|Holly
- project = watch|reels|property
- run_report = link to ops_run_reports row (preferred)
- total_score
- category_scores = JSON/text like:
A:2 B:1 C:2 D:1 E:1 F:2 G:2 H:1
- failed_categories = list
- issues = bullet list of concrete violations
- recommended_fixes = 1â5 prioritized fixes
- runbook_change_request = exact proposed runbook edits (if any)
- status = draft (or sent_to_juniper if urgent)
========================
ESCALATION / CHANGE POLICY
========================
I do not invent new systems. I propose explicit edits to runbooks/contracts.
If repeated violations occur:
- create runbook_change_request
- set compliance_audits.status=sent_to_juniper
- summarize the pattern and the fix
========================
FAIL-SAFE
========================
If I cannot access ops_run_reports, compliance_audits, or agent runbooks:
- I report blocked with a concrete next step and stop.
Judy âreminderâ (paste into Judyâs system prompt header / description)
REMINDER (Judy):
Rehydrate from Ops base â agent_runbooks (Judy + audited agents). Audit ops_run_reports for Rosemary/Olive/Holly using the universal rubric. Write results to Ops base â Audits â compliance_audits (link to ops_run_reports). Focus on compliance + evidence presence, not subjective capability. No secrets in summaries.
Not heartbeat.
- Heartbeat should stay a runtime check-in (âare there stale runs / blocked runs / todayâs digest?â). Itâs the wrong place for long, structural role definitions.
Put Judyâs stuff in two places:
-
Judy runbook (canonical):
Ops base âagent_runbooksâ agent=Judyâ paste the full runbook text there. -
Judy agent config / system prompt (short reminder):
Paste the 2â4 line âREMINDER (Judy)â into whatever field defines Judyâs always-on instructions.
If you have an AGENTS.md-equivalent that all your agents automatically load on startup, thatâs a great place for the global contract + deprecation notice (RunReport+Heartbeat contract, âTrello is backlog not outputâ, âno self-killâ, etc.). But Judyâs detailed rubric and âwrite audits to compliance_auditsâ should still live in her runbook.
So:
- Global invariant rules â AGENTS.md (if itâs universally loaded) + each runbook header
- Judy specifics â Judy runbook + Judy system prompt reminder
- Operational checks â heartbeat (Hazel)
OK, now write me a summary on how to direct the agents to do their jobs, and give them enough information to be proactive and find the things I need
Hereâs the âoperatorâs manualâ for directing your council so theyâre proactive, donât lose context, and you always know what they produced.
1) The mental model (3 layers)
Trello = planning/backlog
- what you want, priorities, WIP limits, milestones
Ops base (NocoDB) = execution visibility
ops_run_reports= what ran, heartbeats, blocked/done, links to outputsagent_runbooks= how each agent operates (persistent âbrainâ)compliance_audits= Judyâs audit results
Project bases (NocoDB) = artifacts
- Reels DB / Watch DB / Property DB tables are the actual work products
Rule: No work is âdoneâ unless it shows up in (a) project tables + (b) ops_run_reports.
2) How you assign work (so agents donât drift)
Use a two-part assignment:
A) Scope Ticket (Juniper writes)
One variable + one quota. Examples:
- Reels:
reel_media_asset_id = 123 - Property:
source_scope = /âŚ/Red House/Contracts/ - Watch: â10 watches from âWatches â Missing Sourcesâ viewâ
B) Task Packet (the standard template)
Every packet must include:
- Scope (what to touch / not touch)
- Quota + stop conditions
- Inputs (views/paths/IDs)
- Writeback targets (exact tables)
- Done criteria (including run report closure)
This prevents âagent spends 6 hours planningâ and forces bounded progress.
3) How agents become proactive (without guessing)
Make each agent pull from a queue view when you donât specify an ID.
Olive (Reels flagship)
Give her a view like:
- âReel Assets â Readyâ (status=new, has source_locator/immich_asset_id)
Then her default behavior is: - take the top 1 asset â process â write scenes/tags â mark needs_review â log run
Rosemary (Watch background)
Give her two views:
- âWatches â Missing Sourcesâ (links_count=0)
- âConflicts â Open Missing Citationâ
Then her default behavior is: - pick 10 watches â normalize searches â promote Watch Links â open cited conflicts â update watch status â log run
Holly (Property background)
Give her:
- âDocs â Unprocessedâ and âReview Inboxâ
Then her default behavior is: - pick one scope â index 200 docs â classify with alias/path scoring â extract vendors/contracts/issues when confident â push ambiguity to Review Inbox â log run
Hazel (Ops)
Give Hazel one job:
- every day: digest + stale-run alerts
Hazel should not do âworkâ; Hazel makes sure work is visible and not silently stuck.
Judy (Auditor)
Judy is proactive by auditing the last N runs and writing to compliance_audits. She doesnât need project specifics; she uses runbooks + the rubric.
4) The two rules that prevent âblocked all nightâ
- RunReport + Heartbeat Contract (every run logs to
ops_run_reports) - No self-kill (slow is fine; checkpoint + ask if suspected hang)
Youâll stop waking up to ânothing happenedâ because ânothing happenedâ becomes a visible blocked/stale run.
5) How you feed agents âthings you needâ (so they can find them)
Agents canât read your mind. Give them:
- a queue (a view/list of next items)
- a definition of âgood enoughâ (quota + safe field whitelist)
- a place to put ambiguity (Review Inbox / needs_review)
If you want them to proactively hunt for âthings you need,â encode it as:
- âFind X, for Y items, store results in Z table, mark uncertain as needs_review.â
Examples:
- âFor 10 watches, find case size + movement type; store citations; conflicts open.â
- âFor 200 property docs, classify property + vendor; contracts/invoices only if confident.â
6) How you add new ideas without derailing work
Use an IDEAS.md / Ideas table per project (or a Trello âIdeasâ list).
Juniperâs weekly job: convert ideas into Task Packets when theyâre actually ready.
If you want one concrete âstart tomorrowâ routine
- Morning: read Hazelâs digest (from ops_run_reports)
- Pick 1 flagship scope for Olive (one reel asset)
- Let Rosemary run 1 watch batch (10 watches)
- Let Holly run 1 property batch (200 docs, one scope)
- End of day: Judy audits last 5 runs and files 3 fixes
If you tell me what your current âready queueâ is for each project (which view/table indicates ânext itemsâ), I can help you define the exact queue views so agents can truly self-serve work without you hand-feeding IDs.
how often shoudl judy run her audit?
Start with daily, then taper to weekly once the system is behaving.
Recommended cadence
Phase 1 (first 1â2 weeks while youâre rolling this out):
- 1Ă per day (after your main overnight runs finish)
- Scope: audit the last 1â2 runs per agent (Rosemary/Olive/Holly) â fast âare we complying?â check
Phase 2 (once compliance is stable):
- 2â3Ă per week for the quick audit, or keep daily if you like the rhythm
- Plus 1Ă per week âdeep auditâ:
- last 5 runs per agent
- identify repeated failures
- propose runbook edits
Event-triggered audits (run immediately)
Have Judy run an extra audit when:
- you change runbooks/contracts significantly
- you see repeated
blockedor stale runs inops_run_reports - an agent starts producing outputs without citations / without Review Inbox routing
The key is: Judy should be frequent enough to catch drift quickly, but bounded so she doesnât become the new bottleneck.
I have to change my approach, my home grown scene detection is not working , so I want to rebuild rosemaryâs pipeline around a new implementation built on https://github.com/ymrohit/openscenesense-ollama can you write a scope ticket and task packet for juniper to direct olive to implement and run this job? Instead of the trello card, can you instead implement this a scope ticket and task packet where the scope ticket is to implement this system in steps and the task packet provides all the instructions? Where the outputs is a list of annotated scenes from processing one of the film reels, starting with a sample of 5 minutes, and if approved, creating a repeatable process with a report for how to process a reel. i need the templates for the scope ticket and task packet.
Below are two templates you can hand to Juniper. The first is the Scope Ticket (multi-step implementation plan). The second is the Task Packet (all execution instructions Olive follows, including the 5âminute sample run + repeatable process/report).
Theyâre written to fit the system we designed: rehydrate from runbook â log to ops_run_reports â bounded batch â artifacts written somewhere visible.
1) SCOPE TICKET (Juniper) â âOpenSceneSense-Ollama Reels Pipeline (5âmin sample â repeatable process)â
SCOPE TICKET (Juniper v1)
Title: Reels pipeline rebuild â OpenSceneSense-Ollama integration (5âmin sample â repeatable process)
Project: reels
Agent: Olive
Related Task Packet: âTASK PACKET: Olive â OpenSceneSense-Ollama sample + repeatable reel processorâ
Single Scope Variable:
- reel_media_asset_id = <PASTE_ID>
OR source_locator / immich_asset_id = <PASTE>
Objective (what success looks like):
A) Process a 5-minute sample from one reel and output a clear list of annotated âscene segmentsâ
B) Get approval
C) Turn it into a repeatable âprocess a reelâ workflow + report/runbook
Implementation steps (do in order; stop if blocked):
Step 1 â Validate prerequisites
- Confirm Ollama is running and ffmpeg is available
- Confirm we can access the reel video source
Step 2 â Install OpenSceneSense-Ollama in an isolated env
- Use a dedicated venv/dir; do not modify system python
- Confirm CLI works: `openscenesense-ollama --schema` (store schema as artifact)
Step 3 â Create a 5-minute clip (non-destructive)
- Extract a 5-minute sample clip from the reel (default start 00:00:00, duration 300s; adjust if needed)
- Save clip path as an artifact
Step 4 â Run OpenSceneSense-Ollama on the clip
- Use CLI with cache dir and JSON output
- Output: result JSON + a human-readable annotated scene list (MD/CSV/JSON)
Step 5 â Review gate (human approval)
- Post links to:
- the sample clip
- the JSON output
- the annotated scene list
- a short âquality notesâ summary
Step 6 â Create repeatable process + report
- Produce a runbook/report that documents:
- exact commands
- parameters (frames_per_minute, min/max frames, selector)
- runtime expectations
- where outputs go
- how to rerun idempotently
- If approved, add a script wrapper (process_reel.py or process_reel.sh) to run on a full reel.
Stop conditions:
- If anything is unclear/missing (Ollama model availability, ffmpeg missing, CLI not working), mark blocked in ops_run_reports with next_step.
2) TASK PACKET (Olive) â âOpenSceneSense-Ollama: sample 5 min + annotated scenes + repeatable reel processâ
TASK PACKET (v1)
Title: Implement + run OpenSceneSense-Ollama reel analysis (5âmin sample â annotated scenes â repeatable process)
Project: reels
Assigned agent: Olive
Runbook: Ops base â agent_runbooks â agent=Olive (must read before starting)
Goal
Replace the failing home-grown scene detection with a new local pipeline based on:
https://github.com/ymrohit/openscenesense-ollama
Deliverables:
1) A 5-minute sample run from one reel that outputs a list of annotated âscene segmentsâ
2) If quality is acceptable: a repeatable process + report (âHow to process a reelâ)
Scope (strict)
IN SCOPE:
- Exactly one reel_media_asset:
reel_media_asset_id = <PASTE_ID> OR source_locator/immich_asset_id = <PASTE>
- Work is limited to:
- installing and validating openscenesense-ollama in an isolated env
- extracting a 5-min clip
- running the analyzer
- producing annotated scene list + report
OUT OF SCOPE:
- Full-library processing
- Rebuilding the entire DB schema
- Any cloud upload of video frames (keep local)
Quota / Stop conditions
- Quota: analyze only a 5-minute clip (300s) for this run
- Time limit: 2â3 hours max (installation + run + report)
- Stop early if: ffmpeg missing, Ollama not reachable, video inaccessible, or CLI fails
Inputs
- Reel source video (from reel_media_asset source_locator/immich_asset_id)
- Local Ollama endpoint (default http://localhost:11434 unless configured otherwise)
- ffmpeg available for clip extraction
Writeback / Outputs (must be visible)
You must produce these artifacts and link them in ops_run_reports:
A) sample_clip.mp4 (5 minutes)
B) openscenesense_output.json (raw analyzer output)
C) annotated_scenes.md (human-readable list of segments)
D) process_reel_report.md (repeatable runbook: commands + parameters + runtime notes)
E) (optional) schema.json from `openscenesense-ollama --schema`
RunReport + Heartbeat (mandatory)
1) Create ops_run_reports row BEFORE doing work:
- project=reels, agent=Olive, status=running
- started_at=now, last_heartbeat_at=now
- progress_unit=steps, progress_total=6, progress_current=0
- summary="OpenSceneSense-Ollama: install â clip â run â annotated scenes â report"
2) Heartbeat:
- Update last_heartbeat_at at least every 30â60 minutes with what step youâre on.
- Never self-kill slow work.
Execution Steps (do in order)
Step 1 â Rehydrate + preflight (progress_current=1)
- Read your runbook row.
- Confirm you can access the reel video source.
- Confirm Ollama is running.
- Confirm ffmpeg is installed.
If any preflight fails:
- ops_run_reports.status=blocked
- blocker="whatâs missing"
- next_step="exact fix needed"
- STOP.
Step 2 â Install OpenSceneSense-Ollama safely (progress_current=2)
- Create an isolated working directory + virtualenv.
- Install `openscenesense-ollama` (prefer pip install unless you need to pin to repo).
- Validate CLI works:
- run `openscenesense-ollama --schema` and save output as artifact (schema.json)
- Log versions/notes in process_reel_report.md.
Step 3 â Create a 5âminute clip (progress_current=3)
- Use ffmpeg to extract a 300s clip from the target reel video.
- Default: start=00:00:00 duration=300s (adjust start if the first 5 minutes are blank leader)
- Save path as artifact and record exact ffmpeg command in report.
Step 4 â Run openscenesense-ollama on the clip (progress_current=4)
- Use CLI with:
- explicit --output result.json
- a --cache-dir so reruns are fast/idempotent
- enable audio only if the reel actually has usable audio
- Save raw JSON output as artifact.
(Do NOT guess models in the report if you didnât verify they exist locally. If you must choose, document what you chose and why.)
Step 5 â Produce âannotated scenesâ list (progress_current=5)
- Convert analyzer output into a clear list of scene-like segments for the 5-minute clip.
- Output format: annotated_scenes.md containing a table like:
- segment #, start_time, end_time (or timestamp window), description, key visual notes, (optional) transcript snippet, confidence/needs_review
- Be honest: if the tool yields âtimeline entriesâ not true cuts, label them as âsegmentsâ not âground-truth scenesâ.
Step 6 â Create repeatable process report (progress_current=6)
Write process_reel_report.md including:
- prerequisites (Ollama running, ffmpeg, python version)
- exact commands for:
- extracting a 5-min sample
- running analysis (with parameters)
- generating annotated_scenes.md
- caching/idempotency notes
- expected runtime per 5 minutes and extrapolated per reel
- what âapproval gateâ looks like before running full reels
- how to rerun safely without duplicating outputs
Closeout
- Set ops_run_reports.status=done
- summary includes:
- links/paths to all artifacts (clip/json/md/report)
- short quality assessment (âgood enough to proceed?â + any tuning needed)
- If blocked: status=blocked with next_step.
Acceptance criteria
- [ ] Sample clip exists and is reproducible from source
- [ ] Raw JSON output saved
- [ ] Annotated scene/segment list produced (human-readable)
- [ ] Repeatable process report written (commands + parameters + runtime)
- [ ] ops_run_reports shows heartbeats + final links
If you want, I can also generate a follow-up Scope Ticket for âfull reel run after approvalâ that turns the above into a repeated batch job (e.g., process in 5â10 minute chunks, append to scenes table, generate an HTML report per reel).
Yes. Please generate the followup scope proposal for the ongoing process of processing every reel and writing all the data into the Reel database
SCOPE TICKET (Juniper v1)
Title: Reels v1 â Ongoing OpenSceneSense-Ollama processing for ALL reels â write annotated segments into Reels DB
Project: reels
Agent: Olive
Prereq: âOpenSceneSense-Ollama 5âminute sample runâ approved
Objective
Build a repeatable, idempotent pipeline that processes every reel_media_asset and writes:
- scene/segment rows with start/end timecodes + descriptions
- provenance/review flags (needs_review)
- artifacts (raw JSON outputs + per-reel report)
âŚand updates reel_media_assets status so the system can run continuously without duplicating work.
Single Scope Variable
- processing_mode = âprocess entire reel library sequentially from queue viewâ
(not ad-hoc IDs)
Definition of Done (v1)
For each reel_media_asset:
1) Every segment of the reel is analyzed (in bounded chunks)
2) Reels DB contains scene rows for those segments with time ranges and human-readable annotations
3) A per-reel report exists (links to artifacts + counts + review notes)
4) reel_media_assets.status is updated to processed/needs_review/error
5) The process can be re-run without duplicating scenes (idempotent keys + cache)
Implementation Plan (steps)
Step 1 â Queue + statuses (Reels DB)
- Add/confirm fields on reel_media_assets:
- status: new | processing | processed | needs_review | error
- processed_at, last_error
- (optional) next_segment_start / progress marker
- Create a saved view: âReel Assets â Readyâ (status=new, has source_locator)
Step 2 â Segment strategy (bounded, restartable)
- Adopt a fixed segment window for v1 (recommended: 5 minutes per chunk)
- segment_duration_s = 300
- segment_overlap_s = 0 (v1; add overlap later if needed)
- For each reel:
- compute segment start times: 0, 300, 600, ⌠until duration
Step 3 â Job tracking + artifacts (Ops visibility)
- For each reel, create a jobs row (job_type=process_reel) and job_runs per attempt.
- For each segment, record a job_items row (entity_type=segment, entity_id = segment index or a synthetic key).
- Save artifacts per segment:
- sample clip path (optional; could be transient)
- openscenesense JSON output path
- Save artifacts per reel:
- reel_report.md/html with links + summary
Step 4 â DB write model (how to store âannotated scenesâ)
- Treat OpenSceneSense output as âsegmentsâ (scene-like units), not ground-truth cuts.
- For each segment, write into scenes:
- scene_key must be idempotent, e.g.:
reel_media_asset_id + â@â + start_timecode + â-â + end_timecode
- start_timecode/end_timecode = segment boundaries
- status = needs_review (default)
- raw_model_response = store relevant JSON excerpt or pointer
- Seed minimal tags:
- scene_tags: add âopenscenesenseâ + âneeds_reviewâ
- Do NOT guess people/events/locations in v1 unless output is explicit and high confidence.
Step 5 â Per-reel report (review package)
For each reel_media_asset, generate:
- reel_report.md (or html) containing:
- reel identifier + source_locator
- run parameters (segment_duration, CLI args, cache dir)
- counts: segments processed, scenes written, errors, needs_review
- links to artifacts (raw JSON, any logs)
- a short âquality notesâ section
Step 6 â Continuous execution policy (so it can run 24/7 safely)
- Concurrency: 1 reel at a time (v1)
- Heartbeat:
- ops_run_reports per reel job
- update at least every 30â60 minutes and per segment completion
- Stop/Block rules:
- if 3 consecutive segment failures â mark reel status=error and block with next_step
- never self-kill slow work; checkpoint + continue
Step 7 â Approval gates
- Gate A: sample 5-minute run approved (already done)
- Gate B: first full reel processed â human review of reel_report + a handful of segments
- Gate C: enable âprocess all reels from queue viewâ mode
Outputs
- Reels DB populated: scenes + scene_tags (and any needed join tables)
- artifacts table populated with JSON outputs + reel reports
- reel_media_assets statuses updated (queueable)
- ops_run_reports shows ongoing progress + no silent stalls
Stop conditions
- Missing prerequisites (ffmpeg/ollama/CLI)
- DB write failures
- Unexpected performance blowups (segment too slow) â reduce segment duration and retry
Notes / Risks
- OpenSceneSense outputs are âtimeline/segmentsâ; treat as reviewable segments for family correction.
- Scene boundaries may be coarse; refinement (true cut detection) can be a v2.
If you want, I can also generate the matching Task Packet Olive uses for the âprocess one full reel in segmentsâ run (the operational script: select next reel from âReel Assets â Readyâ, process segments, write scenes, write reel_report, update statuses, log heartbeats).
Yes please generate it.
TASK PACKET (v1)
Title: Reels v1 â Process reel_media_assets from queue using OpenSceneSense-Ollama (segment batches â write scenes â reel report)
Project: reels
Assigned agent: Olive
Runbook: Ops base â agent_runbooks â agent=Olive (must read before starting)
Goal
Run a repeatable, idempotent pipeline that processes reel_media_assets in bounded segments using OpenSceneSense-Ollama, writes âscene segmentsâ into the Reels DB, and produces a perâreel report artifact + ops_run_reports visibility.
Scope
IN SCOPE:
- Choose reels ONLY from the Reels DB queue view:
âReel Assets â Readyâ (status=new, has source_locator/immich_asset_id)
- Process ONE reel_media_asset at a time.
- Use OpenSceneSense-Ollama locally (no cloud upload).
OUT OF SCOPE:
- Processing multiple reels concurrently
- Face clustering / identity resolution at library scale
- Perfect cut detection (treat outputs as segments for review)
Quota / Stop conditions (bounded run)
- Default segment_duration_s = 300 (5 minutes)
- Per run, process:
- up to 6 segments (â30 minutes of footage) OR
- stop after 3 hours, whichever comes first
- If the reel finishes before limits, you may complete it.
- Stop early if: DB writes fail, Ollama unreachable, ffmpeg missing, source video inaccessible.
Inputs
- Reels DB:
- reel_media_assets queue view âReel Assets â Readyâ
- scenes table (must support start_timecode/end_timecode)
- join tables: scene_tags (+ optionally scene_notes/artifacts/jobs/job_runs/job_items if present)
- Source video via reel_media_assets.source_locator (or immich_asset_id)
- Local tools:
- ffmpeg available for segment clip extraction
- OpenSceneSense-Ollama CLI installed and working
- Ollama running (default localhost)
Writeback targets (must be written every run)
1) reel_media_assets:
- set status to processing at start
- update progress marker (either next_segment_start or notes field) if you have one
- set status to processed/needs_review/error at appropriate times
2) scenes:
For each processed segment, create or upsert a scene row with:
- reel_media_asset_id
- start_timecode / end_timecode (segment boundaries)
- scene_key = deterministic idempotent key, e.g.:
"<reel_media_asset_id>@<start_s>-<end_s>"
- status = needs_review (default)
- raw_model_response (store a short excerpt or a pointer; donât bloat DB if huge)
3) scene_tags:
- Ensure every created scene gets at least:
- âopenscenesenseâ
- âneeds_reviewâ
(If tags donât exist yet, create them.)
4) artifacts:
- Save the raw JSON output for each segment as an artifact with:
artifact_type = âopenscenesense_jsonâ
uri/path = where itâs stored
- Save/update a perâreel report artifact:
artifact_type = âreel_reportâ
uri/path = report md/html file location
5) ops_run_reports (mandatory visibility)
- Every run creates and closes an ops_run_reports row with progress + links.
RunReport + Heartbeat Contract (mandatory)
At start:
- Create ops_run_reports row:
project=reels, agent=Olive, status=running
started_at=now, last_heartbeat_at=now
progress_unit=segments
progress_total=6
progress_current=0
summary="OpenSceneSense reel processing: 6 segments batch (5 min each) from queue"
Heartbeat:
- Update last_heartbeat_at every 30â60 minutes AND after each segment completes.
- Never self-kill slow work. If suspected hang: checkpoint + ask.
Preflight (fail fast)
Before you start processing:
- Confirm OpenSceneSense-Ollama CLI runs (`openscenesense-ollama --schema` works or equivalent).
- Confirm Ollama reachable.
- Confirm ffmpeg available.
- Confirm target reel_media_asset has a usable source_locator/immich asset reference.
If any preflight fails:
- ops_run_reports.status=blocked
- blocker + next_step
- STOP.
Execution Steps (one run)
Step 1 â Pick next reel from queue
- Select the top reel_media_asset from âReel Assets â Readyâ.
- Set reel_media_assets.status = processing.
- Create a jobs/job_runs entry if those tables exist:
- job_type=process_reel, target_id=reel_media_asset_id, status=running
Step 2 â Determine next segment start
- If a progress marker exists (next_segment_start / last_processed_time), start there.
- Otherwise start at 0 seconds.
- Compute segment windows: [t, t+300), then next.
Step 3 â For each segment (up to 6 this run)
For segment i:
A) Extract clip (non-destructive)
- Use ffmpeg to create a temporary segment clip file.
- Record the exact command in the perâreel report.
B) Run OpenSceneSense-Ollama on the clip
- Use cache dir so reruns are idempotent.
- Output JSON to a deterministic path, e.g.:
cache/<reel_media_asset_id>/<start_s>-<end_s>.json
- Save JSON path as an artifacts row (openscenesense_json).
C) Convert output â âscene segmentâ row(s)
- For v1, create exactly ONE scene row per segment window:
- start_timecode = start_s
- end_timecode = end_s
- description/summary from OpenSceneSense output (brief)
- status=needs_review
- If the tool provides finer timeline entries and youâre confident mapping them, you may create multiple scene rows, but keep it deterministic and idempotent.
D) Tag + provenance
- Add tags: openscenesense + needs_review to scene_tags.
E) Update progress + heartbeat
- Increment ops_run_reports.progress_current by 1
- Update last_heartbeat_at and summary counts.
Step 4 â Update reel status after this batch
- If you reached end of reel and all segments processed:
- set reel_media_assets.status = needs_review (recommended default) or processed
- If not finished:
- keep status=processing (or set back to new with a progress marker, depending on your schema)
- record next segment start marker.
Step 5 â Write/update perâreel report artifact
Create or update reel_report.md/html with:
- reel_media_asset_id + source
- parameters: segment_duration_s, cache dir, CLI args
- segments processed this run (start/end list)
- # scenes upserted
- errors/notes
- what needs review
Store as artifacts row (reel_report) and link it in ops_run_reports.
Closeout
- If successful: ops_run_reports.status=done with:
- segments processed count
- scenes upserted count
- link to reel_report artifact
- link to âNeeds Review scenesâ filtered to reel_media_asset_id
- If blocked/error:
- set reel_media_assets.status=error (only if truly unrecoverable)
- ops_run_reports.status=blocked with blocker + next_step
- do not silently stop.
Acceptance Criteria
- [ ] Reel chosen from queue, marked processing
- [ ] Up to 6 segments processed, each produces deterministic scene rows
- [ ] Every scene tagged openscenesense + needs_review
- [ ] Raw JSON outputs saved + linked as artifacts
- [ ] Perâreel report updated + linked
- [ ] ops_run_reports shows heartbeats + final summary/links
If you want, I can also give you a tiny âReel Assets â Readyâ view spec (exact filters/sorts) and a suggested progress marker field (next_segment_start_s) so Olive can resume midâreel cleanly without reprocessing.
Yes. Who do I give rhe view spec to? Juniper or olive?
Give the view spec to Juniper.
Juniper should own the queue definitions (what counts as âreadyâ, what order to process, what fields must be present), because thatâs portfolio / system coordination. Then:
- Juniper creates the views in the Reel Database base
- Juniper pastes the view link(s) into Oliveâs runbook (so Olive can rehydrate after resets)
- Olive just consumes the view (âpick top row, process, update status/progressâ)
If Juniper is busy, Olive can create the view as a one-off, but the key is: Juniper should canonize it in the runbook so it doesnât drift.
Can you write the prompt for juniper to implement the spec and install it into run book?
You are Juniper. Task: implement the Reels queue âview specâ in NocoDB and install the resulting view links + rules into Oliveâs runbook so Olive can run continuously without being hand-fed IDs.
CONTEXT
We are processing reels using OpenSceneSense-Ollama in bounded 5âminute segments.
Olive should select work from a canonical queue view, process one reel at a time, and update statuses/progress markers.
PART 1 â IMPLEMENT REQUIRED FIELDS (additive only)
In the Reel Database base, table: reel_media_assets (or your equivalent)
Ensure these columns exist (add if missing):
- status (select): new | processing | processed | needs_review | error
- priority (integer, default 0)
- next_segment_start_s (integer, default 0) // progress marker for resume
- segment_duration_s (integer, default 300) // optional per-asset override
- last_processed_at (datetime nullable)
- last_error (long text nullable)
Also ensure these columns exist and are populated:
- source_locator (text) and/or immich_asset_id (text)
- reel_id link (if you have physical reels table)
PART 2 â CREATE CANONICAL QUEUE VIEWS (NocoDB saved views)
Create these saved views in the Reel Database base:
1) View: âReel Assets â Readyâ
Filter:
- status = new
- AND (source_locator not empty OR immich_asset_id not empty)
Sort:
- priority desc
- created_at asc (or id asc) for deterministic order
Columns to show:
- id, reel_id/reel_code, source_locator/immich_asset_id, priority, next_segment_start_s, segment_duration_s
2) View: âReel Assets â Processingâ
Filter: status = processing
Sort: last_processed_at asc (oldest first)
3) View: âReel Assets â Needs Reviewâ
Filter: status = needs_review
Sort: last_processed_at desc
4) View: âReel Assets â Errorâ
Filter: status = error
Sort: last_processed_at desc
Optional but useful:
5) View: âScenes â Needs Review (Recent)â
Filter: scenes.status = needs_review (or tag contains needs_review)
Sort: created_at desc
(If possible, include reel_media_asset_id column for filtering.)
PART 3 â DEFINE STATUS TRANSITION RULES (write into Olive runbook)
Define these rules and add them to Oliveâs runbook:
- Olive selects top row from âReel Assets â Readyâ.
- Immediately sets status â processing.
- Processes segments starting at next_segment_start_s in 300s windows (unless segment_duration_s override exists).
- After each segment: update next_segment_start_s += segment_duration_s and last_processed_at=now.
- If reel complete: status â needs_review (default) or processed (only if you want).
- If 3 consecutive segment failures OR unrecoverable error: status â error and set last_error with next_step.
PART 4 â INSTALL VIEW LINKS INTO OLIVE RUNBOOK (persistent)
In Ops base â agent_runbooks â agent=Olive:
- Add a section âQueue Views (Canonical)â and paste the direct links or precise navigation names for:
- Reel Assets â Ready
- Reel Assets â Processing
- Reel Assets â Needs Review
- Reel Assets â Error
- (Optional) Scenes â Needs Review (Recent)
Also add a short âHow to start a runâ checklist:
1) Open Reel Assets â Ready
2) Pick top asset
3) Set status=processing
4) Create ops_run_reports row
5) Process max 6 segments per run
6) Close ops_run_reports with reel_report link + counts
DELIVERABLE
- Post an ops_run_reports row (project=ops) confirming:
- fields added (if any)
- views created (names)
- Olive runbook updated with view links + rules
If you want, I can also provide the exact âOlive runbook sectionâ text to paste (so Juniper doesnât have to write it).
Yes please provide that text
========================
OLIVE RUNBOOK ADDENDUM â Reels Queue + OpenSceneSense Segment Processing (v1)
========================
Purpose
Process reel_media_assets continuously using OpenSceneSense-Ollama in bounded 5âminute segments, writing reviewable âscene segmentsâ into the Reels DB with full visibility via ops_run_reports.
Canonical Queue Views (Reel Database base)
(These are the ONLY approved entry points for selecting work.)
- Reel Assets â Ready: <PASTE_VIEW_URL_OR_NAV_PATH>
- Reel Assets â Processing: <PASTE_VIEW_URL_OR_NAV_PATH>
- Reel Assets â Needs Review: <PASTE_VIEW_URL_OR_NAV_PATH>
- Reel Assets â Error: <PASTE_VIEW_URL_OR_NAV_PATH>
Optional:
- Scenes â Needs Review (Recent): <PASTE_VIEW_URL_OR_NAV_PATH>
Required Fields on reel_media_assets
- status: new | processing | processed | needs_review | error
- priority (int)
- source_locator and/or immich_asset_id (must be non-empty to process)
- next_segment_start_s (int; default 0)
- segment_duration_s (int; default 300)
- last_processed_at (datetime)
- last_error (text)
Default Processing Parameters (v1)
- segment_duration_s = 300 (5 minutes) unless reel_media_assets.segment_duration_s overrides
- segments_per_run = 6 (â30 minutes of footage) OR stop at 3 hours
- concurrency = 1 reel at a time
- Treat OpenSceneSense output as âsegmentsâ (reviewable units), not perfect cut detection.
Status Transition Rules (do not deviate)
1) Pick work ONLY from âReel Assets â Readyâ (status=new).
2) Immediately set status â processing when starting a reel.
3) After each segment completes successfully:
- next_segment_start_s += segment_duration_s
- last_processed_at = now
4) When reel is fully processed:
- status â needs_review (default) OR processed (only if explicitly desired)
5) On failure:
- Write last_error with actionable message + next step
- If failure is transient, keep status=processing and retry next run
- If 3 consecutive segment failures or unrecoverable error â status=error
RunReport + Heartbeat Contract (mandatory)
Every run MUST create and close an ops_run_reports row:
- project=reels, agent=Olive, status=running at start
- progress_unit=segments, progress_total=6, progress_current increments per segment
Heartbeat:
- update last_heartbeat_at every 30â60 minutes AND after each segment completes
Never self-kill slow work; if suspected hang, checkpoint + ask.
How to Run One Batch (standard operating procedure)
1) Open âReel Assets â Readyâ, select the top reel_media_asset.
2) Confirm source_locator/immich_asset_id present; read next_segment_start_s.
3) Set reel_media_assets.status=processing.
4) Create ops_run_reports row (running) with summary including reel_media_asset_id.
5) Process up to 6 segments starting at next_segment_start_s:
For each segment:
a) Extract clip (ffmpeg) for [start, start+segment_duration_s)
b) Run OpenSceneSense-Ollama with cache-dir and JSON output
c) Save JSON output as an artifact (openscenesense_json)
d) Upsert ONE scene row per segment (v1) with:
- scene_key = "<reel_media_asset_id>@<start>-<end>"
- start_timecode/end_timecode = segment boundaries
- status=needs_review
- raw_model_response = short excerpt or pointer
e) Add tags to scene_tags: openscenesense + needs_review
f) Update next_segment_start_s and heartbeat
6) Update/write per-reel report artifact (reel_report.md/html) and link it.
7) Close ops_run_reports (done/blocked) with counts + links to views/artifacts.
Writeback Contract (must be true after every run)
- Every processed segment creates/upserts deterministic scene rows (no duplicates).
- Every new scene is clearly marked needs_review and tagged openscenesense.
- Raw JSON outputs are saved and linked as artifacts.
- ops_run_reports includes links to reel report and relevant views.
Blocker Policy
If anything prevents safe progress (missing ffmpeg, Ollama unreachable, DB writes failing, missing schema):
- Set ops_run_reports.status=blocked with blocker + exact next_step
- Do not continue or improvise risky changes.
Yes â and Juniperâs instinct (Option A1) is the right simplification.
1) First: treat that token as burned
You pasted a live NocoDB API token into a public thread. Rotate/revoke it ASAP (and delete the message if you can). Going forward, tell Juniper: never print tokens/IDs in chat outputsâonly say âtoken found / storedâ and reference where it lives.
2) Simplest path: stop trying to automate NocoDB view creation
NocoDBâs âview via APIâ surface is fiddly and not worth it for 4 views. Do:
- You manually create the views in the UI from the spec (15 minutes)
- Juniper then:
- updates Oliveâs runbook with view names + navigation path (not URLs)
- seeds the other runbooks
- posts an ops_run_reports summary
You donât actually need view URLs if your runbook says:
âNocoDB â Reel Database â reel_media_assets â View: âReel Assets â Readyââ.
3) Prompt to give Juniper to simplify (copy/paste)
Juniper â simplify this job immediately:
1) STOP all NocoDB API automation for view creation. Assume views will be created manually in the UI.
2) SECURITY: never print or log tokens, API keys, base IDs, table IDs, or secrets in chat output. If you need them, store privately and only report âconfiguredâ.
3) Deliverables for this task:
A) A plain-text View Spec for each required view (filters, sorts, columns), written for the NocoDB UI.
B) Update Oliveâs runbook using VIEW NAMES + navigation path (no URLs required).
C) Seed missing agent_runbooks rows and prepend the deprecation notice + RunReport contract.
D) Create one ops_run_reports entry (project=ops) summarizing what is done + what the human must do (manual view creation checklist).
4) DO NOT generate extra documentation files unless explicitly requested. Keep output minimal and actionable.
Success = user can create views in UI quickly and Olive can run from the queue reliably.
If you want, paste here the exact list of the 4 views Juniper wants (names + their filter/sort rules from her SETUP doc), and Iâll sanity-check them for âwill this actually queue work correctly?â before you spend the 15 minutes in the UI.
Can you verify this? âââ
VIEW SPECS (above â ready to copy into NocoDB UI)
4 views to create in Reel Archive base â reel_media_assets table:
- "Reel Assets â Ready" (your work queue)
⢠Filter: status = new AND source_locator not empty
⢠Sort: priority DESC, created_at ASC - "Reel Assets â Processing" (monitor in-progress)
⢠Filter: status = processing
⢠Sort: last_processed_at ASC - "Reel Assets â Needs Review" (completed, awaiting approval)
⢠Filter: status = needs_review
⢠Sort: last_processed_at DESC - "Reel Assets â Error" (failed reels)
⢠Filter: status = error
⢠Sort: last_processed_at DESC
âââ
SQL MIGRATIONS (above â ready to run in PostgreSQL)
Adds 9 required fields to reel_media_assets table:
- status (enum: new | processing | processed | needs_review | error)
- priority (integer, for queue ordering)
- next_segment_start_s (progress marker for resumption)
- segment_duration_s (per-reel override, default 300s)
- last_processed_at (timestamp for monitoring)
- last_error (error message storage)
- source_locator (video file path)
- immich_asset_id (Immich reference)
- duration_seconds (reel duration)
All migrations are additive only (no destructive changes). Indexes added for performance.
âââ
Next steps:
- Copy VIEW SPECS into NocoDB UI â create the 4 views (15 min)
- Run SQL MIGRATIONS on Reels DB (5 min)
- Verify "Reel Assets â Ready" shows queued reels
Then Olive can run Phase 2! đ
Mostly yes â this is very close to what you want. Iâd make two small tweaks so you donât accidentally starve the queue or break NocoDB compatibility.
View specs: one important filter tweak
Right now âReadyâ requires source_locator not empty. But you also have immich_asset_id, and some reels may only have that.
Recommended âReel Assets â Readyâ filter:
status = new- AND (
source_locatornot empty ORimmich_asset_idnot empty)
Everything else looks good:
- Ready sort:
priority DESC, created_at ASCâ (ifcreated_atexists) - Processing sort:
last_processed_at ASCâ - Needs Review sort:
last_processed_at DESCâ - Error sort:
last_processed_at DESCâ
One practical note: if created_at doesnât exist in that table, sort by id ASC as the stable tiebreaker.
SQL migrations: fields are right, but be careful with Postgres ENUM
The field list matches the design (status, priority, next_segment_start_s, segment_duration_s, last_processed_at, last_error, source_locator, immich_asset_id, duration_seconds) â
The potential gotcha is using a Postgres enum for status. Some tools/UIs (including low-code DB UIs) can be finicky with enums.
Safer option:
- make
statusa TEXT column - add a CHECK constraint limiting values to
new|processing|processed|needs_review|error
That keeps it âenum-likeâ without UI weirdness.
Optional (nice-to-have)
- Add an index on
(status, priority, created_at)to keep the Ready view fast â - Consider a
processed_attimestamp if you want âProcessed Todayâ views.
If you apply the one filter change (Ready uses source_locator OR immich_asset_id), youâre good to proceed with the UI view creation and Oliveâs queue-based processing.
Can you provide the proper feedback prompt with your fixes and suggestions so juniper can update her files before we execute or just rewrite the files
Hereâs a copy/paste âfeedback + fixupâ prompt you can give Juniper so she updates her files before you execute anything. It includes the specific corrections (Ready view filter + enum warning) and a couple safety tweaks (no tokens in outputs).
Prompt to Juniper: update the files with fixes (views + migrations + runbook/task packet)
Juniper â please do a fixup pass on the Reels queue/view + migrations deliverables before execution.
CRITICAL SAFETY RULE (must apply immediately)
- Do NOT print, paste, or store API tokens/base IDs/table IDs in chat outputs or docs intended to be shared.
- Treat any token previously posted as compromised; recommend rotation and remove it from any files/logs.
FILES TO UPDATE (you created these):
- SETUP-nocodb-views.md
- SETUP-reels-queue-migrations.sql
- RUNBOOK-ADDENDUM-reels-queue-views.md
- TASK-PACKET-reels-v1-segment-batch-run.md
FIX #1 â Ready view filter must allow immich_asset_id OR source_locator
In SETUP-nocodb-views.md and RUNBOOK-ADDENDUM, update âReel Assets â Readyâ to:
Filter:
- status = new
- AND (source_locator not empty OR immich_asset_id not empty)
Reason: some assets will only have immich_asset_id and would be incorrectly excluded.
Also add a note:
- If created_at is not available on reel_media_assets, use id ASC as the stable tiebreaker instead of created_at.
FIX #2 â Avoid Postgres ENUM for status (prefer TEXT + CHECK)
In SETUP-reels-queue-migrations.sql:
- Prefer: status TEXT with a CHECK constraint limiting allowed values:
new, processing, processed, needs_review, error
Instead of: creating a Postgres enum type.
Reason: low-code UIs (including NocoDB) can be finicky with Postgres enum types; TEXT+CHECK is more compatible.
Implementation detail:
- Use additive SQL patterns where possible:
ALTER TABLE ... ADD COLUMN IF NOT EXISTS ...
and name the CHECK constraint explicitly.
FIX #3 â Ensure the Task Packet matches the queue reality
In TASK-PACKET-reels-v1-segment-batch-run.md:
- When selecting an asset, explicitly say:
âAsset is eligible if source_locator OR immich_asset_id is present.â
- Add a small âfail-fastâ preflight reminder:
If neither is present â mark blocked and stop.
OPTIONAL NICE-TO-HAVE (only if easy)
- Consider adding processed_at (timestamp) to reel_media_assets so âProcessed Todayâ views are easy.
- Ensure recommended indexes exist for queue performance:
(status, priority, created_at/id) and (status, last_processed_at)
DELIVERABLE
- Update the four files accordingly.
- Produce a short status note summarizing:
what changed + why + what the human needs to do next (manual NocoDB view creation).
If you want me to rewrite the files verbatim (as full replacements) I can do that tooâbut Iâd need you to paste Juniperâs current file contents (or at least the relevant sections) since I donât have access to her local output.
OK, These new job descriptions are working well, can we now clean up hazels tasks? She needs to clean up all the crons and launch agents, and store them in the database. She needs a Base for her data, and Also to understand how to report what happeend in her daily, and weekly, maintenance runs. She will need a RUNBOOK and files to set up her job as OPS/IT Agent, that maintains all the systems like immich and nocodb, and reboots anythign that has gone down.
Youâre ready for the next maturation step: Hazel stops being âa pile of cronsâ and becomes an Ops system with an inventory + run logs + incident trail. The pattern is the same one thatâs working for the other agents:
- Ops base = source of truth
- ops_run_reports = every run is visible
- Inventory tables = Hazel can be proactive
Below is a clean, minimal setup (you can expand later).
1) Hazel should use the existing Ops base (no new base needed)
Use the shared Ops base and add a group like âOps Inventoryâ.
Hazelâs job is cross-project (Immich/NocoDB/Postgres/Ollama/etc.), so her data belongs next to:
ops_run_reportsagent_runbookscompliance_audits
2) Minimal tables Hazel needs (start with 3)
A) ops_services (service registry)
One row per system you care about: Immich, NocoDB, Postgres, Ollama, OpenClaw gateway, etc.
Suggested columns:
service_name(unique)host(text; âlocalâ, âserver1â, etc.)category(storage/db/ai/app/monitoring)healthcheck_method(text; âhttpâ, âprocessâ, âdb queryâ, etc.)healthcheck_target(url/port/process name)restart_method(text; âlaunchdâ, âdockerâ, âsystemdâ, âmanualâ)can_auto_restart(boolean, default true)can_auto_reboot_host(boolean, default false)desired_state(up)last_seen_healthy_at(datetime)
last_restart_at(datetime)notes
B) ops_scheduled_jobs (crons/maintenance jobs)
One row per cron/maintenance run Hazel owns.
Columns:
job_name(unique)schedule(text: cron string / âdaily 06:00â)job_type(daily/weekly/adhoc)script_or_command_ref(text/path)enabled(boolean)depends_on_services(text or links)last_run_report(link âops_run_reports)failure_policy(alert-only / retry / restart-service / block)
C) ops_launch_agents (for macOS launchd / LaunchAgents, if you use them)
Columns:
label(unique)plist_pathowner_service(link â ops_services)enabled(boolean)last_loaded_atnotes
Optional later: ops_incidents (outages) and ops_changes (config changes), but donât start there.
3) Hazel reporting (daily + weekly) using the same contract
Hazel should always write:
- an
ops_run_reportsrow per run (daily/weekly/adhoc) - link it to the relevant
ops_scheduled_jobs.last_run_report - include: what was checked, what failed, what was restarted, what is blocked, and the next step
Guardrail (important)
Default policy should be:
- auto-restart services: yes (bounded retries)
- auto-reboot hosts: no (only with explicit approval and runbook rule)
This prevents âhelpfulâ destructive ops.
4) Hazel RUNBOOK seed (paste into Ops â agent_runbooks â agent=Hazel)
If you want, I can write the full runbook text, but the key sections are:
- Mission: âkeep systems running; visibility firstâ
- Inventory is the source of truth: ops_services + ops_scheduled_jobs + ops_launch_agents
- RunReport + heartbeat contract
- Restart policy + max retries
- Escalation policy (blocked instead of risky actions)
- Daily run checklist
- Weekly run checklist
5) What to do next (very concrete)
- Juniper creates the 3 tables above in Ops base and seeds initial rows (Immich, NocoDB, Postgres, Ollama, OpenClaw gateway, etc.)
- Hazel gets a âDaily Ops Maintenanceâ Task Packet (bounded, reports to ops_run_reports)
- Hazel gets a âWeekly Ops Maintenanceâ Task Packet (cleanup + audit)
If you want, I can generate three copy/paste prompts next:
- Juniper prompt to create the 3 ops tables + seed rows + views (âServices Downâ, âJobs Failingâ, âLaunchAgents Missingâ)
- Hazel runbook text (complete)
- Hazel daily + weekly execution prompts (bounded, with safe restart rules)
Tell me: are you on macOS (LaunchAgents) only, or a mix of macOS + Linux/docker for these services? That affects how we describe restart_method fields.
yes geenrate the three prompts, and make sure you tell her to stop following the old instructions for the ACT protocol.
Below are the three copy/paste prompts you asked for. Theyâre written to (a) set up Hazel as an Ops/IT agent with inventory + run logging + safe remediation, and (b) explicitly stop following the old ACT protocol.
Prompt 1 â Juniper: create Ops Inventory tables + seed + views + wire into Hazel runbook
You are Juniper. Task: set up Hazel as the Ops/IT agent with an Ops Inventory in NocoDB (Ops base), plus canonical views and runbook wiring.
CRITICAL DEPRECATION (system-wide)
- Any old ACT v2 / 3-gate capability protocol is deprecated.
- Do NOT follow or reference old ACT instructions in Ops workflows.
WHERE THIS LIVES
NocoDB â Ops base (shared)
Create groups:
- âOps Inventoryâ
- âOps Viewsâ
TABLES TO CREATE (Ops Inventory)
A) ops_services
Columns:
- id (auto)
- service_name (text unique)
- host (text; e.g., local/server name)
- category (select: db | storage | app | ai | monitoring | other)
- healthcheck_method (select: http | process | db_query | manual)
- healthcheck_target (text) // url/port/process name
- restart_method (select: launchd | docker | systemd | manual | other)
- restart_ref (text) // pointer to script/command/runbook step (donât store secrets)
- can_auto_restart (boolean, default true)
- can_auto_reboot_host (boolean, default false)
- desired_state (select: up | down)
- last_seen_healthy_at (datetime nullable)
- last_restart_at (datetime nullable)
- notes (long text nullable)
B) ops_scheduled_jobs
Columns:
- id (auto)
- job_name (text unique)
- schedule (text) // cron string or âdaily 06:00â
- job_type (select: daily | weekly | adhoc)
- script_or_command_ref (text) // pointer to script/runbook step
- enabled (boolean)
- depends_on_services (long text or links if you prefer)
- last_run_report (link â ops_run_reports) // preferred
- failure_policy (select: alert_only | retry | restart_service | block)
- notes (long text nullable)
C) ops_launch_agents
Columns:
- id (auto)
- label (text unique)
- plist_path (text)
- owner_service (link â ops_services, nullable)
- enabled (boolean)
- last_loaded_at (datetime nullable)
- notes (long text nullable)
SEED INITIAL ROWS (minimum viable)
In ops_services, add rows for at least:
- Immich
- NocoDB
- PostgreSQL
- Ollama
- OpenClaw Gateway (or your agent runtime service)
(Leave healthcheck_target/restart_ref blank if unknown; set can_auto_restart=false until verified.)
In ops_scheduled_jobs add:
- Hazel Daily Ops Maintenance (daily)
- Hazel Weekly Ops Maintenance (weekly)
VIEWS TO CREATE (Ops Views)
1) âServices â Down or Unknownâ
Filter:
- desired_state = up AND (last_seen_healthy_at is empty OR last_seen_healthy_at older than X hours)
Sort:
- last_seen_healthy_at asc
2) âJobs â Enabledâ
Filter: enabled = true
Sort: job_type asc, job_name asc
3) âJobs â Last Run Blocked/Failedâ
Filter: last_run_report.status in (blocked, failed) (or equivalent)
Sort: last_run_report.finished_at desc
4) âLaunchAgents â Disabledâ
Filter: enabled = false
Sort: label asc
WIRE INTO HAZEL RUNBOOK
In Ops base â agent_runbooks â agent=Hazel:
- Add a section âCanonical Ops Inventory Linksâ listing:
- ops_services (and the âServices â Down or Unknownâ view)
- ops_scheduled_jobs (and âJobs â Last Run Blocked/Failedâ view)
- ops_launch_agents (and âLaunchAgents â Disabledâ view)
DELIVERABLE
- Post one ops_run_reports row (project=ops) confirming:
tables created, views created, Hazel runbook updated with navigation paths.
Prompt 2 â Hazel runbook text (paste into Ops â agent_runbooks â agent=Hazel)
HAZEL RUNBOOK v2.0 â Ops/IT Agent (Systems Reliability)
Effective: immediately
========================
DEPRECATION NOTICE (IMPORTANT)
========================
Old ACT v2 / 3-gate capability protocol is deprecated.
I do NOT run ACT tests or capability scoring. I focus on Ops reliability:
inventory, health, bounded maintenance runs, safe remediation, and visibility.
Trello is backlog/planning only. Ops visibility is in ops_run_reports + Ops Inventory tables.
========================
MISSION
========================
Keep core systems healthy (Immich, NocoDB, Postgres, Ollama, OpenClaw gateway, etc.).
Make all maintenance visible and auditable.
Prefer safe, bounded actions. If unsure, alert and mark blocked.
========================
SOURCE OF TRUTH (CANONICAL)
========================
NocoDB â Ops base:
- ops_run_reports (every run is logged + heartbeats)
- ops_services (service registry)
- ops_scheduled_jobs (cron/job registry)
- ops_launch_agents (launchd registry, if applicable)
- agent_runbooks (procedures)
- compliance_audits (Judy audits)
========================
SAFETY / REMEDIATION POLICY
========================
- Auto-restart services is allowed ONLY if ops_services.can_auto_restart=true AND restart_ref is known/verified.
- Auto-reboot host is NOT allowed by default (can_auto_reboot_host=false). Escalate instead.
- Never kill âslowâ work just because itâs slow. Checkpoint + report.
- Never include secrets/tokens in summaries or logs.
Max retries:
- Restart attempts per service per run: 2
- If still unhealthy: mark blocked and create an incident note in ops_run_reports.
========================
RUNREPORT + HEARTBEAT CONTRACT (MANDATORY)
========================
Every Hazel run (daily/weekly/adhoc) must:
- Create ops_run_reports row before starting (status=running)
- Heartbeat every 30â60 minutes
- Close done/blocked with: what was checked, what changed, what is still broken, next_step
- Link to relevant NocoDB views
========================
DAILY OPS MAINTENANCE (bounded)
========================
Goal: detect outages early, fix safe ones, and report clearly.
Inputs:
- Ops Views: âServices â Down or Unknownâ, âJobs â Last Run Blocked/Failedâ, âLaunchAgents â Disabledâ
Actions:
1) Check services health (from ops_services / view)
2) For each unhealthy service:
- If can_auto_restart=true and restart_ref exists: attempt restart (max 2)
- Update last_restart_at + last_seen_healthy_at if recovered
- If not recovered: mark blocked with next_step
3) Check jobs that failed/blocked; do NOT silently ignore
4) Produce a daily digest summary (in ops_run_reports)
========================
WEEKLY OPS MAINTENANCE (bounded)
========================
Goal: reduce drift and restore reliability.
Actions:
- Inventory reconciliation: ensure ops_services/jobs/launch_agents match reality
- Remove/disable duplicate or obsolete scheduled jobs (with human approval if risky)
- Review recurring failures patterns and propose runbook updates
- Confirm backups/log retention processes (if you have them)
- Produce a weekly summary report (ops_run_reports)
========================
BLOCKER PROTOCOL
========================
If missing info prevents safe action:
- ops_run_reports.status=blocked
- blocker = what is missing
- next_step = exact human action needed
Stop. Do not guess.
Prompt 3 â Hazel execution prompts (daily + weekly) to run as jobs
HAZEL DAILY OPS MAINTENANCE â EXECUTION PROMPT
0) Rehydrate
- Open Ops base â agent_runbooks â Hazel row (source of truth)
- Open Ops Views:
âServices â Down or Unknownâ
âJobs â Last Run Blocked/Failedâ
âLaunchAgents â Disabledâ
1) Start ops_run_reports (mandatory)
Create ops_run_reports row:
- project=ops, agent=Hazel, status=running
- started_at=now, last_heartbeat_at=now
- progress_unit=checks, progress_total=4
- summary="Daily Ops Maintenance: health checks + safe restarts + report"
2) Check services (progress=1)
For each service flagged down/unknown:
- If can_auto_restart=false or restart_ref missing:
- record as blocked (needs human/runbook update), do NOT attempt restarts
- Else attempt restart up to 2 times
- Record outcome per service (recovered vs still down)
- Update ops_services.last_seen_healthy_at / last_restart_at accordingly
3) Check scheduled jobs health (progress=2)
- Review âJobs â Last Run Blocked/Failedâ
- For each:
- record failure pattern + next_step
- do NOT auto-edit job definitions unless explicitly safe and instructed
4) Check launch agents (progress=3)
- Review âLaunchAgents â Disabledâ
- If any are critical and should be enabled, raise as action item (donât guess)
5) Closeout digest (progress=4)
- Update ops_run_reports.status=done/blocked
- Include counts:
services checked, restarts attempted, services recovered, still broken, jobs failing
- Include links to the three views above
- Never paste secrets/tokens
Heartbeat rule:
- Update last_heartbeat_at every 30â60 minutes (even if slow).
STOP if blocked:
- status=blocked + blocker + next_step
HAZEL WEEKLY OPS MAINTENANCE â EXECUTION PROMPT
0) Rehydrate (Hazel runbook + Ops Views)
1) Start ops_run_reports (project=ops, unit=checks, total=5)
2) Inventory reconciliation:
- ops_services has entries for all critical systems
- can_auto_restart flags are conservative (false unless verified)
3) Cron/job cleanup:
- list enabled jobs in ops_scheduled_jobs
- identify duplicates/obsolete; propose disable actions (donât do risky deletes automatically)
4) Reliability improvement:
- summarize recurring failures + propose 1â3 runbook edits
5) Close ops_run_reports with weekly summary + links + proposed changes
If you tell me whether your services are primarily macOS LaunchAgents, Docker, or a mix, I can tighten restart_method/restart_ref guidance so Hazelâs remediation stays safe and actually works.
its a mix. We use orbstack instead of Docker. It is also hard to keep track of all the websites that are spun up in the course of projects, some of those shoudl run persistently, other can be spun up when needed. We really need a global table of all websites, ports and URLS that Hazel can manage to prevent port conflicts and to shut down deprecated sitres
A mix + OrbStack + lots of ad-hoc web servers is exactly where an Ops inventory pays off. You want one canonical âwhatâs running on what port, and should it be running?â table in the Ops base, and Hazel should treat it like a registry.
Add one global table in Ops base: ops_websites (or ops_endpoints)
Keep ops_services for âsystemsâ (Immich, Postgres, NocoDB, etc.). Add a second table specifically for HTTP endpoints because one service can expose multiple URLs.
Recommended table: ops_websites (simple + effective)
Key idea: enforce uniqueness on (host, port) so port conflicts become visible immediately.
Columns (suggested):
site_name(unique human name)project(select: reels | watch | property | ops | other)owner_agent(select: Hazel/Juniper/Olive/âŚ)runtime(select: orbstack | launchd | node | python | manual | other)service_ref(link â ops_services, nullable)host(text: local / machine name)port(integer)protocol(http/https)
base_path(text nullable)internal_url(text)external_url(text nullable) (if exposed)exposure(select: local_only | lan | public)persistence(select: persistent | on_demand | ephemeral)desired_state(select: up | down)actual_state(select: up | down | unknown)healthcheck_url(text nullable)startup_ref(text) (pointer to launchd label, orbstack container name, script path, etc.)shutdown_ref(text nullable)last_seen_up_at(datetime nullable)deprecated(boolean)deprecated_reason(text nullable)notes
Rule: any time someone spins up a site that matters, it gets registered here.
Views Hazel should use daily
Create these saved views in Ops base:
- Websites â Persistent (Should Be Up)
- Filter:
persistence=persistent AND desired_state=up AND deprecated=false
- Websites â Port Conflicts
- Group by
host + port - Show groups where count > 1
(If NocoDB canât do this cleanly, Hazel can still spot it by sorting host+port and scanning duplicates.)
- Websites â Deprecated But Still Up
- Filter:
deprecated=true AND actual_state=up
(This is Hazelâs âshut down old stuffâ queue.)
- Websites â Unknown Health
- Filter:
desired_state=up AND (last_seen_up_at empty OR last_seen_up_at older than X hours)
How Hazel uses it to prevent chaos
- Before starting any new web server: check
ops_websitesfor host+port availability. - If a site is âephemeralâ: register it but set
persistence=ephemeralanddesired_state=downby default. - If a site should persist: set
persistence=persistent+desired_state=upand add a realhealthcheck_url.
OrbStack integration (conceptually)
In ops_services.restart_method, add orbstack as an allowed value. For websites running inside OrbStack, startup_ref can be the container name + port mapping. Hazelâs runbook should say: ârestart using the runtime method; if unknown â alert/block, donât guess.â
If you want, I can generate a Juniper prompt to add the ops_websites table + the 4 views above, and a Hazel runbook addendum that says âall websites must be registered; daily run checks conflicts + deprecated sites.â
yes please generate these prompts.
Here are the two prompts (plus an optional third âdaily prompt patchâ) to implement the global websites/ports registry and wire it into Hazelâs Ops workflow.
Prompt 1 â Juniper: create ops_websites table + views + wire into Hazel runbook
You are Juniper. Task: add a global registry of websites/ports/URLs to prevent port conflicts and enable Hazel to shut down deprecated sites.
CRITICAL DEPRECATION (system-wide)
- Old ACT v2 / 3-gate protocol is deprecated. Do not follow it or reference it in Ops workflows.
WHERE THIS LIVES
NocoDB â Ops base (shared)
Create a group: âOps Inventoryâ (if not already)
Create a group: âOps Viewsâ (if not already)
TABLE TO CREATE: ops_websites (Ops Inventory)
Columns:
- id (auto)
- site_name (text unique)
- project (select: reels | watch | property | ops | other)
- owner_agent (select: Hazel | Juniper | Olive | Rosemary | Holly | Judy | other)
- runtime (select: orbstack | launchd | node | python | manual | other)
- service_ref (link â ops_services, nullable)
- host (text) // e.g., local machine name
- port (integer)
- protocol (select: http | https)
- base_path (text nullable)
- internal_url (text)
- external_url (text nullable)
- exposure (select: local_only | lan | public)
- persistence (select: persistent | on_demand | ephemeral)
- desired_state (select: up | down)
- actual_state (select: up | down | unknown)
- healthcheck_url (text nullable)
- startup_ref (text) // e.g., OrbStack container name, launchd label, script path
- shutdown_ref (text nullable)
- last_seen_up_at (datetime nullable)
- deprecated (boolean, default false)
- deprecated_reason (text nullable)
- notes (long text nullable)
IMPORTANT CONSTRAINT (best effort)
- Enforce uniqueness of (host, port) if your tooling supports it.
If not, at least create a view to surface duplicates.
VIEWS TO CREATE (Ops Views)
1) âWebsites â Persistent (Should Be Up)â
Filter:
- persistence = persistent
- desired_state = up
- deprecated = false
2) âWebsites â Deprecated But Still Upâ
Filter:
- deprecated = true
- actual_state = up
3) âWebsites â Unknown Healthâ
Filter:
- desired_state = up
- AND (last_seen_up_at is empty OR last_seen_up_at older than 6 hours)
4) âWebsites â Port Conflictsâ
Implementation options:
- If grouping is supported: group by host+port and show groups where count > 1
- Otherwise: sort by host asc, port asc and visually spot duplicates
SEED ROWS (minimum)
- Add rows for the websites you already know are important (NocoDB, Immich, any project UIs).
- If unknown, create placeholders with desired_state=down and notes âneeds discoveryâ.
WIRE INTO HAZEL RUNBOOK
In Ops base â agent_runbooks â agent=Hazel:
- Add a section âWebsites/Ports Registry (Canonical)â that:
- states ops_websites is the source of truth for ports/URLs
- links to the four views above
- defines policy: persistent vs on_demand vs ephemeral; how to mark deprecated
DELIVERABLE
- Post one ops_run_reports row (project=ops) summarizing:
ops_websites table created, views created, Hazel runbook updated.
Prompt 2 â Hazel runbook addendum text (paste into Hazel runbook)
========================
HAZEL RUNBOOK ADDENDUM â Websites/Ports Registry (Ops base â ops_websites)
========================
Problem
We run a mix of OrbStack containers, launch agents, and ad-hoc dev servers. Ports and URLs drift, causing conflicts and forgotten âzombieâ sites.
Canonical Source of Truth
NocoDB â Ops base â Ops Inventory â table `ops_websites`
Policy
- Any website/UI that matters must be registered in ops_websites.
- Every entry must include host + port + internal_url.
- persistence:
- persistent: should be up all the time (monitor + restart if safe)
- on_demand: only run when needed (keep desired_state=down by default)
- ephemeral: temporary; should generally be desired_state=down after use
- deprecated=true means the site should be shut down and removed from active use.
Daily Ops Maintenance (additional checks)
During the daily run, I must review these views:
- Websites â Persistent (Should Be Up)
- Websites â Deprecated But Still Up
- Websites â Unknown Health
- Websites â Port Conflicts
Remediation rules
- If a persistent site is down and can_auto_restart is known/verified via ops_services:
attempt safe restart (bounded retries).
- If a deprecated site is still up:
do NOT guess shutdown steps; follow shutdown_ref if defined.
If shutdown_ref is missing, mark blocked and request a proper shutdown procedure.
- If port conflicts exist:
mark blocked with the conflicting entries and propose a port reassignment plan.
- Never reboot a host automatically. Escalate if required.
Reporting
- Every daily/weekly maintenance run must include:
- count of websites checked
- list of persistent sites down
- list of deprecated sites still up
- any port conflicts found
- next steps
All written to ops_run_reports with links to the relevant ops_websites views.
(Optional) Prompt 3 â Patch Hazel daily execution prompt to include websites checks
If you want Hazelâs daily prompt updated, prepend this section after âCheck servicesâ:
WEBSITES/PORTS CHECK (must do daily)
- Open Ops views:
âWebsites â Persistent (Should Be Up)â
âWebsites â Deprecated But Still Upâ
âWebsites â Port Conflictsâ
- Record findings in ops_run_reports summary.
- If deprecated-but-up and shutdown_ref exists: execute shutdown safely; otherwise block.
- If port conflicts: block with the exact conflicting host+port entries and recommend resolution.
If you tell me roughly how many âsitesâ you currently have (order of magnitude) and whether most are OrbStack or launchd, I can suggest a simple naming convention for startup_ref/shutdown_ref so it stays consistent (and actually executable).
for the daily and weekly execution prompts, is this somethign that Juniper prompts to Hazel, and hazel stores? or is there a different way to set itup?
Best setup is: Juniper seeds it once, Hazel runs it forever from durable storage. You donât want daily/weekly prompts living only in chat.
Recommended structure (durable + low-drift)
1) Hazel runbook = the full procedure (canonical)
In agent_runbooks (Hazel row), keep the complete:
- Daily Ops Maintenance procedure
- Weekly Ops Maintenance procedure
- Policies (safe restart rules, no reboot by default, reporting format)
Thatâs the âhowâ.
2) ops_scheduled_jobs = the job registry + schedule + pointer
Each job row (Daily / Weekly) contains:
- schedule (âdaily 06:00â, âweekly Sun 09:00â)
- enabled flag
script_or_command_ref/prompt_refthat points to the invocation text
Thatâs the âwhat/whenâ.
3) The actual âexecution promptâ should be tiny
Instead of pasting a giant prompt every time, the scheduled invocation can be:
âHazel: run Daily Ops Maintenance as defined in your runbook; log to ops_run_reports; update ops_scheduled_jobs.last_run_report.â
This is the ârun it nowâ trigger.
Who does what
- Juniper: creates the
ops_scheduled_jobsrows (Daily/Weekly), sets schedules, and ensures Hazelâs runbook has the procedures + links to views. - Hazel: executes the job using the runbook, writes
ops_run_reports, updatesops_scheduled_jobs.last_run_report. - (Optional) Scheduler/Cron/LaunchAgent: triggers the tiny invocation prompt on schedule.
If you donât have automation yet
You can still run it manually:
- Juniper (or you) sends Hazel the tiny invocation prompt
- Hazel follows the runbook
Once itâs stable, automate the trigger. The key is: the logic lives in the runbook, not in the trigger prompt.
If you tell me roughly how many âsitesâ you currently have (order of magnitude) and whether most are OrbStack or launchd, I can suggest a simple naming convention for startup_ref/shutdown_ref so it stays consistent (and actually executable). : We have between 20-40 sites currently, but most of them are deprecated. most are launchd, maybe 7 are on orbstack. I also wanted Holly's management to include services like Ollama and MQTT (running natively)and Immich and Open-webUI (ruinning on orbstack)
Goodâ20â40 sites is exactly the range where a naming convention stops being âniceâ and becomes survival.
1) First correction: services belong to Hazel, not Holly
Hollyâs domain is property documents + vendors/contracts/issues.
Ollama/MQTT/Immich/Open-WebUI are Ops services, so they should live under:
ops_services(service registry) withowner_agent=Hazelops_websites(HTTP endpoints) linked back viaservice_ref
Holly can consume those services, but she shouldnât manage them.
2) Naming convention for startup_ref / shutdown_ref (consistent + executable)
Use a typed prefix so Hazel knows what sheâs looking at without guessing.
Launchd (most of yours)
startup_ref:launchd:<label>shutdown_ref:launchd:<label>
Examples:
launchd:com.machinefiction.nocodblaunchd:com.machinefiction.reels-adminlaunchd:com.machinefiction.mqtt
OrbStack (your ~7)
startup_ref:orbstack:<container_name>shutdown_ref:orbstack:<container_name>
Examples:
orbstack:immichorbstack:open-webui
(If you run multiple stacks/compose projects, add a namespace: orbstack:<stack>/<container>.)
Manual scripts (when you donât want commands in the DB)
startup_ref:script:<path-or-repo-ref>shutdown_ref:script:<path-or-repo-ref>
This keeps secrets out of the DB and lets you version-control the actual actions.
3) Naming convention for site_name (human friendly + sortable)
Iâd do:
<project>-<purpose>-<env>
Examples:
ops-nocodb-prodops-immich-prodops-openwebui-prod
reels-admin-devwatch-dashboard-dev
This makes it obvious whatâs deprecated and what must stay up.
4) How to model your specific services
ops_services
- Ollama â
restart_method=launchd(or manual),can_auto_restart=trueonly if verified - MQTT â same
- Immich â
restart_method=orbstack - Open-WebUI â
restart_method=orbstack
ops_websites
Add endpoints that users hit:
- Immich web UI (port/url)
- Open-WebUI (port/url)
- NocoDB UI (port/url)
âŚand link each website row to itsservice_ref.
5) Handling âmost are deprecatedâ
Make it explicit so Hazel can clean them up safely:
deprecated=truedesired_state=downdeprecated_reasonfilled- (optional)
sunset_date
Then Hazelâs weekly job is literally: âDeprecated But Still Up â shut down using shutdown_ref or block if missing.â
If you want, I can write a Juniper prompt to add these conventions into the ops_websites table (including runtime-based validation rules) and a Hazel runbook addendum that defines exactly how to use them during daily/weekly runs.
[Bug] Subagents nĂŁo retornam respostas
Descrição:
- Subagents completam tarefas (status: done)
- Tokens sĂŁo processados (8-18k por tarefa)
- Mas respostas NĂO aparecem no chat
- Verificado nos logs: agent responde "FUNCIONOU!" mas nĂŁo chega ao usuĂĄrio
Ambiente:
- OpenClaw version: 2026.2.26
- Runtime: subagent
- Channel: Telegram
Passos para reproduzir:
- Criar subagent com sessions_spawn
- Executar tarefa simples
- Verificar que resposta nĂŁo chega
Expected: Resposta do agent no chat
Actual: Sem resposta, mas status = "done"
Isso bate com um bug que jĂĄ apareceu em Telegram: o subagent termina (âdoneâ), logs mostram que respondeu, mas a mensagem nĂŁo ĂŠ entregue no chat.
Tem uma issue bem parecida (e menciona reprodução em 2026.2.26) aqui:
- https://github.com/openclaw/openclaw/issues/31714 â âAnnounce-mode delivery from subagents/crons logs success but never reaches Telegramâ
SugestĂŁo prĂĄtica:
- Comente nessa issue com o seu caso (pra nĂŁo fragmentar): versĂŁo 2026.2.26, Telegram, runtime=subagent, e um trecho curto dos logs mostrando o âFUNCIONOU!â/tentativa de envio + o chat_id (sem tokens).
- Workaround enquanto isso nĂŁo ĂŠ corrigido:
- Evite subagents para respostas âdiretas ao usuĂĄrioâ no Telegram: rode a tarefa no agente principal ou
- Faça o subagent escrever o resultado num artefato (arquivo/DB) e tenha o agente principal (ou um job Hazel) mandar o resumo no Telegram (assim vocĂŞ nĂŁo depende do âannounce deliveryâ do subagent).
Se vocĂŞ colar (redigido) 20â40 linhas do gateway log ao redor do momento em que o subagent ârespondeâ, dĂĄ pra ver se ele estĂĄ falhando no passo de delivery (e.g. enqueue ok, send falha, account routing, etc.).
The last two messages seem to be a mistake, or cross posted from a different forum in a different language. Can you ignore them, and generate a prompt to implment the points 2-5 in your message to me about Naming convention for startup_ref / shutdown_ref (consistent + executable)
Yep â ignoring the cross-posted messages.
Hereâs a copy/paste prompt for Juniper to implement points 2â5 (startup/shutdown naming conventions + site naming + modeling services/endpoints) in your Ops base.
Prompt for Juniper: implement naming conventions + seed core services/endpoints
You are Juniper. Task: implement consistent, executable naming conventions for ops_websites startup_ref/shutdown_ref and standardize site_name, then seed core services/endpoints (Ollama, MQTT, Immich, Open-WebUI). This must be durable and safe.
CONTEXT
- Mixed runtime: mostly launchd (LaunchAgents), some OrbStack.
- We want a global registry to prevent port conflicts, identify deprecated sites, and allow Hazel to manage safely.
- No secrets/tokens should ever be stored in refs or summaries.
PART A â INSTALL THE CONVENTIONS (document + enforce)
1) In Ops base (NocoDB), create a short doc row or add a runbook section (Hazel runbook addendum is fine) defining:
startup_ref/shutdown_ref format (typed prefixes):
- launchd:<label>
- orbstack:<container_name>
- script:<path-or-repo-ref>
Rules:
- If runtime=launchd â startup_ref MUST start with launchd:
- If runtime=orbstack â startup_ref MUST start with orbstack:
- If runtime=manual â prefer script: refs; do not store raw commands with secrets
2) Define site_name naming convention:
- <project>-<purpose>-<env>
Examples:
- ops-nocodb-prod
- ops-immich-prod
- ops-openwebui-prod
- reels-admin-dev
Record these conventions in:
- Ops base â agent_runbooks â Hazel (and optionally Juniper) so Hazel rehydrates them.
PART B â UPDATE ops_websites TABLE DATA MODEL (if needed)
Verify ops_websites includes (add if missing):
- site_name (unique)
- project, owner_agent
- runtime
- host, port, protocol, internal_url
- persistence (persistent/on_demand/ephemeral)
- desired_state, actual_state
- deprecated (boolean), deprecated_reason
- service_ref (link â ops_services)
- startup_ref, shutdown_ref
- healthcheck_url, last_seen_up_at
PART C â NORMALIZE EXISTING SITES (20â40 sites, most deprecated)
1) For each existing ops_websites row:
- Ensure startup_ref/shutdown_ref uses typed prefix based on runtime:
- launchd:<label> or orbstack:<container>
- Normalize site_name to the convention (<project>-<purpose>-<env>).
- If the site is deprecated:
- deprecated=true
- desired_state=down
- fill deprecated_reason
- If the site should persist:
- persistence=persistent
- desired_state=up
2) Create/maintain views (Ops base â Ops Views):
- Websites â Persistent (Should Be Up)
- Websites â Deprecated But Still Up
- Websites â Port Conflicts
- Websites â Unknown Health
PART D â SEED CORE OPS SERVICES (ops_services) + LINK ENDPOINTS
In ops_services create/verify rows:
- Ollama
- restart_method=launchd (if itâs a LaunchAgent) else manual
- can_auto_restart=false until restart_ref is verified
- MQTT
- restart_method=launchd/manual (as applicable)
- can_auto_restart=false until verified
- Immich
- restart_method=orbstack
- can_auto_restart=false until verified
- Open-WebUI
- restart_method=orbstack
- can_auto_restart=false until verified
For each service, add at least one ops_websites row (endpoint) if it has a web UI:
- Immich UI: ops_websites row with service_ref=Immich, runtime=orbstack, startup_ref=orbstack:<container>, port/url filled
- Open-WebUI: ops_websites row with service_ref=Open-WebUI, runtime=orbstack, startup_ref=orbstack:<container>, port/url filled
- NocoDB UI (if applicable): service_ref=NocoDB, runtime=launchd or other, startup_ref=launchd:<label>
NOTE:
- If exact ports/urls are not known yet, create placeholders with desired_state=down and notes âneeds discoveryâ; do not guess.
PART E â WIRE INTO HAZEL (so she can operate)
Update Hazel runbook:
- Add a section âWebsite Registry Rulesâ:
- typed refs
- site_name convention
- deprecated handling
- Add links/navigation to the four ops_websites views.
DELIVERABLE
- Post one ops_run_reports row (project=ops) summarizing:
- conventions installed (where documented)
- # websites normalized
- which core services/endpoints were seeded
- what fields remain âneeds discoveryâ (ports/urls/labels/containers)
- links to the ops_websites views
If you want, paste 2â3 examples of your actual LaunchAgent labels and OrbStack container names, and Iâll propose a concrete mapping for Immich/Open-WebUI/NocoDB so the initial seed rows are correct (and not placeholders).
Already have the real data from the discovery run. Here are the actuals:
LaunchAgent labels (from launchctl list):
com.ollama.ollama â Ollama
ai.openclaw.gateway â OpenClaw Gateway
com.juniper.homeassistant â Home Assistant
com.juniper.flying-time-blind â Music for Flying
com.juniper.watch-vault â Watch Vault
homebrew.mxcl.mosquitto â MQTT
com.juniper.dashboard-server â Token Dashboard
OrbStack container names (from docker ps -a):
nocodb â NocoDB, port 9090
immich_server â Immich, port 2283
immich_machine_learning â Immich ML
immich_postgres â Postgres, port 5432
immich_redis â Redis, port 6379
open-webui â Open-WebUI, port 3010 (exited)
So the mapping I already seeded is accurate â no placeholders:
Site runtime startup_ref
ops-nocodb-prod orbstack orbstack:nocodb
ops-immich-prod orbstack orbstack:immich_server
ops-openwebui-prod orbstack orbstack:open-webui
ops-ollama-prod launchd launchd:com.ollama.ollama
ops-gateway-prod launchd launchd:ai.openclaw.gateway
ops-mqtt-prod launchd launchd:homebrew.mxcl.mosquitto
All already written to NocoDB. The only ones still marked "needs discovery" are Token Dashboard's startup command (plist exists but I didn't inspect its full ProgramArguments) and Home Assistant's actual health status (LaunchAgent shows exit -9).
This is excellent discovery data â and it means youâre basically past the âplaceholdersâ phase.
A couple practical next steps (and one mindset change):
1) Donât block on âstartup commandâ for LaunchAgents
For Hazelâs purposes, the restart handle is the label:
startup_ref = launchd:<label>shutdown_ref = launchd:<label>
You donât need ProgramArguments to restart/monitor a LaunchAgent; thatâs just ânice documentationâ. So Token Dashboard doesnât need to be âneeds discoveryâ for restartability â only for port/URL/healthcheck if you want it in ops_websites.
If you want to capture ProgramArguments anyway, add an optional field like:
startup_details(long text) onops_servicesorops_launch_agents.
2) Normalize what goes in ops_services vs ops_websites
ops_services should include everything that runs
- launchd services: Ollama, OpenClaw gateway, Home Assistant, Watch Vault, Token Dashboard, Mosquitto
- OrbStack containers: nocodb, immich_server, open-webui, immich_postgres, immich_redis, immich_machine_learning
ops_websites should include only HTTP endpoints
Create website rows for:
- NocoDB: port 9090
- Immich: port 2283
- Open-WebUI: port 3010
- Ollama API (optional as âwebsiteâ): port 11434 (itâs HTTP)
Do not put Postgres/Redis/MQTT ports in ops_websites (theyâre not âsitesâ); keep them as services.
3) Set desired_state/persistence based on what you actually want
Given your note âmost sites are deprecatedâ, Iâd do:
- For truly deprecated:
deprecated=true,desired_state=down,persistence=ephemeral/on_demand - For core infra (NocoDB/Immich/Postgres/Redis/Ollama/Gateway):
persistence=persistent,desired_state=up
Open-WebUI currently âexitedâ:
- If you want it persistent: set
desired_state=upand let Hazelâs daily run attempt an auto-restart (bounded retries). - If itâs optional: set
persistence=on_demand,desired_state=down, and only start when needed.
4) Home Assistant exit -9 should become an incident
âexit -9â smells like a crash/kill. Hazel should:
- mark it
actual_state=down - set a
last_error/incident note - not keep retrying forever (cap retries, then block with next_step like âinspect logs / check memory pressureâ).
If you want, I can write a short âJuniper reconciliation promptâ that tells her exactly how to update:
ops_servicesrows for each label/containerops_websitesrows for the 4 HTTP endpoints- Hazel runbookâs âknown services/endpointsâ section
- and the daily run rule for âOpen-WebUI is downâ vs âHome Assistant crash loopâ handling.
can you write a fully updated promt for this Prompt for Juniper: implement naming conventions + seed core services/endpoints now you know the real names od the launchd and servicesand include the Juniper reconciliation prompt
You are Juniper. Task: fully reconcile and standardize Ops Inventory now that we have REAL discovery data (launchd labels + OrbStack containers). Implement naming conventions (startup_ref/shutdown_ref + site_name) AND perform a reconciliation pass to ensure ops_services/ops_websites/ops_launch_agents are correct, non-placeholder, and usable by Hazel.
CRITICAL SAFETY + DEPRECATION
- Old ACT v2 / 3-gate protocol is deprecated. Do not follow or reference it.
- Do NOT print or paste API tokens, base IDs, table IDs, secrets, or credentials in any chat output or docs.
- Use labels/container names as refs; do not store raw commands with secrets.
DISCOVERY INPUT (authoritative)
LaunchAgent labels:
- com.ollama.ollama (Ollama)
- ai.openclaw.gateway (OpenClaw Gateway)
- com.juniper.homeassistant (Home Assistant; currently exit -9)
- com.juniper.flying-time-blind (Music for Flying)
- com.juniper.watch-vault (Watch Vault)
- homebrew.mxcl.mosquitto (MQTT / Mosquitto)
- com.juniper.dashboard-server (Token Dashboard)
OrbStack containers:
- nocodb (NocoDB) port 9090
- immich_server (Immich) port 2283
- immich_machine_learning (Immich ML)
- immich_postgres (Postgres) port 5432
- immich_redis (Redis) port 6379
- open-webui (Open-WebUI) port 3010 (currently exited)
========================================================
PART 1 â INSTALL/CONFIRM NAMING CONVENTIONS (Durable)
========================================================
A) startup_ref / shutdown_ref typed prefixes (consistent + executable)
Use:
- launchd:<label>
- orbstack:<container>
- script:<path-or-repo-ref>
Rules:
- If runtime=launchd â startup_ref MUST be launchd:<label>
- If runtime=orbstack â startup_ref MUST be orbstack:<container>
- shutdown_ref should mirror startup_ref unless thereâs a distinct stop mechanism
- Do not store ProgramArguments/commands as the primary ref; store them only as optional âstartup_detailsâ text (no secrets)
B) site_name convention
Use:
- <project>-<purpose>-<env>
Examples:
- ops-nocodb-prod
- ops-immich-prod
- ops-openwebui-prod
- ops-ollama-prod
- ops-watchvault-prod
Document these conventions in:
- Ops base â agent_runbooks â Hazel row (canonical)
(Optionally also in Juniper runbook.)
========================================================
PART 2 â RECONCILIATION PASS (Ops Inventory tables)
========================================================
A) ops_services (service registry)
Ensure an ops_services row exists and is correct for each discovered service/container:
Launchd services (restart_method=launchd, restart_ref=launchd:<label>):
- Ollama â launchd:com.ollama.ollama
- OpenClaw Gateway â launchd:ai.openclaw.gateway
- Home Assistant â launchd:com.juniper.homeassistant
- Watch Vault â launchd:com.juniper.watch-vault
- MQTT (Mosquitto) â launchd:homebrew.mxcl.mosquitto
- Token Dashboard â launchd:com.juniper.dashboard-server
- Music for Flying â launchd:com.juniper.flying-time-blind (mark deprecated unless required)
OrbStack services (restart_method=orbstack, restart_ref=orbstack:<container>):
- NocoDB â orbstack:nocodb
- Immich Server â orbstack:immich_server
- Immich ML â orbstack:immich_machine_learning
- Immich Postgres â orbstack:immich_postgres
- Immich Redis â orbstack:immich_redis
- Open-WebUI â orbstack:open-webui
For each ops_services row:
- Set can_auto_restart conservatively:
- true only if Hazel has a verified restart procedure for that runtime
- otherwise false until verified
- Set can_auto_reboot_host=false (default)
- Healthchecks: do not guess unknown URLs.
- If you know the port from discovery and itâs a web service, itâs okay to set healthcheck_method=http and healthcheck_target to the base URL.
- If unknown, set healthcheck_method=manual and leave target blank.
Special handling:
- Home Assistant exit -9: set last_error note + mark actual health unknown/down; do NOT enable aggressive auto-restart loops.
- Open-WebUI exited: mark actual state down; keep desired_state based on whether we want it persistent.
B) ops_websites (HTTP endpoints registry)
Create/verify ops_websites rows ONLY for HTTP endpoints (not DB/Redis/MQTT ports):
Create/verify these âopsâ sites (service_ref should link to ops_services):
1) ops-nocodb-prod
- runtime=orbstack, startup_ref=orbstack:nocodb
- port=9090, protocol=http
- internal_url=http://<HOST>:9090
- persistence=persistent, desired_state=up
- healthcheck_url=http://<HOST>:9090 (ok)
2) ops-immich-prod
- runtime=orbstack, startup_ref=orbstack:immich_server
- port=2283, protocol=http
- internal_url=http://<HOST>:2283
- persistence=persistent, desired_state=up
- healthcheck_url=http://<HOST>:2283 (ok)
3) ops-openwebui-prod
- runtime=orbstack, startup_ref=orbstack:open-webui
- port=3010, protocol=http
- internal_url=http://<HOST>:3010
- persistence=on_demand OR persistent (choose explicitly)
- desired_state=up if you want it always available; otherwise down
- actual_state currently down (container exited)
4) ops-ollama-prod (optional but useful as endpoint)
- runtime=launchd, startup_ref=launchd:com.ollama.ollama
- port=11434, protocol=http
- internal_url=http://<HOST>:11434
- persistence=persistent, desired_state=up
Do NOT create ops_websites entries for:
- immich_postgres:5432, immich_redis:6379, mosquitto:1883 (those are services, not websites)
Port conflict prevention:
- Ensure host+port uniqueness is enforced or at least visible via a view.
C) ops_launch_agents
Ensure ops_launch_agents rows exist for each launchd label:
- label + enabled status + (plist_path if known)
- owner_service link to ops_services where applicable
========================================================
PART 3 â DEPRECATION CLEANUP (sites)
========================================================
Given you have 20â40 sites and most are deprecated:
- For any non-core site that should not persist:
- deprecated=true
- desired_state=down
- persistence=ephemeral or on_demand
- fill deprecated_reason
- Core infra stays deprecated=false and desired_state=up.
========================================================
PART 4 â WIRE INTO HAZEL (Runbook + Daily/Weekly procedures)
========================================================
Update Ops base â agent_runbooks â Hazel row:
- Add section âKnown Services (Discovered)â listing the canonical service names and their restart_ref formats
- Add section âWebsites/Ports Registry (ops_websites)â with:
- naming convention
- views to monitor:
Websites â Persistent (Should Be Up)
Websites â Deprecated But Still Up
Websites â Unknown Health
Websites â Port Conflicts
- Add explicit guardrails:
- never reboot host automatically
- bounded restart retries
- block when refs missing
DELIVERABLE
- Post one ops_run_reports row (project=ops) summarizing:
- conventions installed
- ops_services reconciled (counts)
- ops_websites reconciled (counts)
- what remains truly âneeds discoveryâ (ideally only healthcheck URLs where unknown)
- Home Assistant exit -9 noted + next step
- Open-WebUI exited noted + desired_state decision needed
If you tell me the hostnames you want to use in internal_url (e.g., localhost, mac-mini, home-server), I can also suggest a consistent host value scheme so Hazel can filter by machine cleanly.
From TOOLS.md and what I know:
Machine localhost Tailscale short Tailscale FQDN
chekov (Mac Studio, this machine) localhost / 127.0.0.1 chekov chekov.tail39fdfd.ts.net
scotty (Mac Mini) â scotty scotty.tail39fdfd.ts.net
pike (MacBook Pro, Greenwood Lake) â pike pike.tail39fdfd.ts.net
phillip (MacBook) â phillip phillip.tail39fdfd.ts.net
Here's the full live picture:
Machine Tailscale IP Short name Status
chekov (Mac Studio, this machine) 100.76.67.111 chekov â
online
scotty (Mac Mini) 100.99.242.125 scotty â
active (direct LAN)
pike (MacBook Pro, Greenwood Lake) 100.97.251.103 pike â
active (relay nyc)
phillip (MacBook) 100.80.114.126 phillip đ´ offline (11 min ago)
aphex (iOS) 100.69.143.121 aphex đ¤ idle (iPhone)
bones (iOS) 100.67.84.87 bones đ´ offline (21h ago â ipad)
chekov-pt 100.76.131.67 chekov-pt đ´ offline (24 days â a second chekov profile?)
phillips-macbook-pro 100.67.61.82 phillips-macbook-pro đ´ offline (16 days â old entry?)
This is great â you can make host/URL handling consistent with one simple rule:
Recommended host scheme (so Hazel can operate across machines)
1) Add an ops_hosts table (optional but worth it with 4+ Macs)
In Ops base, create ops_hosts:
host(short name, unique):chekov,scotty,pike,philliptailscale_iptailscale_fqdn(e.g.chekov.tail39fdfd.ts.net)status_hint(optional: online/offline/unknown)notesdeprecated(for old entries likechekov-pt,phillips-macbook-pro)
Then in ops_services / ops_websites, replace free-text host with:
host_ref(link â ops_hosts)
This prevents typos and makes views like âall sites on scottyâ trivial.
2) What should internal_url be?
Use this rule:
-
If the service is intended to be reachable from other machines:
internal_url = Tailscale FQDN
Example:http://chekov.tail39fdfd.ts.net:9090 -
If itâs truly local-only (binds to 127.0.0.1):
internal_url = http://localhost:PORT and setexposure=local_only
(Optionally store the Tailscale URL in notes, or add atailscale_urlcolumn.)
Given youâre doing ops across devices, Iâd default to Tailscale FQDN whenever it works.
3) Clean up the âduplicatesâ
Entries like chekov-pt / phillips-macbook-pro should be marked deprecated=true in ops_hosts so they donât pollute dashboards.
If you want a quick Juniper prompt: âcreate ops_hosts, seed chekov/scotty/pike/phillip with IP+FQDN, deprecate old entries, then convert ops_websites.host to host_ref and update internal_url to use tailscale_fqdnâ.
yes, please do that. 2 things to add. For pike and scotty, we have nodes running with port numbers. pike and chekov run gateways on port 18888, and need https to connect. But all the other sites on chekov dont' support https, and only reliably work with http:// is there something I should do globally to fix https on chekov, or is this normal expected behavior for a primarily internal server with no external interner exposure?
A) âPlease do thatâ â prompt for Juniper (ops_hosts + host_ref + gateway https ports)
Hereâs a copy/paste prompt for Juniper to implement the host registry + wire it into ops_services/ops_websites, including the gateway HTTPS port 18888 on chekov + pike:
You are Juniper. Task: implement ops_hosts and normalize host/URLs across ops_services and ops_websites using the real Tailscale data. Also add explicit OpenClaw gateway endpoints (https:18888) for chekov and pike.
SAFETY
- Do not paste tokens/IDs/secrets in outputs.
- Additive changes only.
PART 1 â Create ops_hosts (Ops base â Ops Inventory)
Create table: ops_hosts
Columns:
- id (auto)
- host (text unique) // chekov, scotty, pike, phillip
- tailscale_ip (text)
- tailscale_fqdn (text) // *.tail39fdfd.ts.net
- status_hint (select: online | offline | unknown)
- deprecated (boolean)
- notes (long text)
Seed rows (active):
- chekov â 100.76.67.111 â chekov.tail39fdfd.ts.net â online
- scotty â 100.99.242.125 â scotty.tail39fdfd.ts.net â online
- pike â 100.97.251.103 â pike.tail39fdfd.ts.net â online
- phillip â 100.80.114.126 â phillip.tail39fdfd.ts.net â offline
Seed rows (deprecated/old):
- chekov-pt â deprecated=true
- phillips-macbook-pro â deprecated=true
(and any other stale entries)
PART 2 â Update ops_websites to use host_ref + consistent internal_url
In ops_websites:
- Add host_ref (link â ops_hosts) if missing
- Keep existing host text for now (non-destructive), but start filling host_ref everywhere.
- Update internal_url to prefer Tailscale FQDN when the service should be reachable from other machines.
Example: http://chekov.tail39fdfd.ts.net:9090
PART 3 â Add/normalize gateway endpoints (HTTPS required)
Create/verify ops_websites entries (HTTP endpoints registry):
- ops-openclaw-gateway-chekov
host_ref=chekov, protocol=https, port=18888
internal_url=https://chekov.tail39fdfd.ts.net:18888
persistence=persistent, desired_state=up
- ops-openclaw-gateway-pike
host_ref=pike, protocol=https, port=18888
internal_url=https://pike.tail39fdfd.ts.net:18888
persistence=persistent, desired_state=up
(Do not change other chekov sites to https unless we explicitly add a proxy later.)
PART 4 â Views to support Hazel
Create views:
- Hosts â Offline (status_hint=offline AND deprecated=false)
- Websites â Persistent (Should Be Up)
- Websites â Port Conflicts (group by host_ref + port if possible)
- Websites â HTTPS Required (protocol=https)
PART 5 â Wire into Hazel runbook
Update Ops base â agent_runbooks â Hazel:
- Add âHost Registryâ section (ops_hosts + key views)
- Add rule: internal_url should use tailscale_fqdn for persistent endpoints when possible
- Add explicit note: gateways require https:18888; other internal sites may be http
DELIVERABLE
- Post ops_run_reports (project=ops) summary with links to ops_hosts and the key website views.
B) Should you âfix HTTPS globally on chekovâ?
What youâre seeing is normal.
On a private/internal network (especially Tailscale-only), itâs common that:
- some components require HTTPS (your gateways on
:18888) - many internal dashboards are HTTP only (NocoDB/Immich/etc.) and work fine that way
You do not need to âmake everything HTTPSâ just because one service requires it.
When you should unify HTTPS
Only if youâre hitting real pain:
- browser mixed-content issues
- clients that refuse HTTP
- you want a consistent âalways httpsâ rule
The clean way to unify (without changing every app)
Use a single TLS-terminating proxy on chekov (or Tailscale âserveâ) that:
- serves
https://chekov.tailâŚand forwards to localhttp://localhost:9090, etc. - keeps everything private (no public internet exposure required)
That gives you HTTPS everywhere without modifying each service.
For now, the simplest + safest ops move is: store protocol per endpoint in ops_websites and let Hazel respect it (HTTP for most, HTTPS for gateways).
OK juiper has done all of these things: Done. Full summary:
Part 1 â ops_hosts (m3v8nif91smphze) â
â 8 rows seeded with Tailscale IPs + FQDNs. phillips-macbook-pro marked deprecated.
Part 2 â ops_websites updated â
â host_ref column added; all 11 existing rows updated; internal_url now uses chekov.tail39fdfd.ts.net FQDNs throughout.
Part 3 â Gateway endpoints â
ops-openclaw-gateway-chekov â https://chekov.tail39fdfd.ts.net:18888 (actual_state=up)
ops-openclaw-gateway-pike â https://pike.tail39fdfd.ts.net:18888 (actual_state=unknown)
â ď¸ My notes say Poppy runs on port 18789 â you should verify whether pike uses 18888 or 18789 before Hazel does anything with it
Part 4 â Views â
Hosts â Offline configured on ops_hosts default view (vwmeo618le70dx9d)
ops_websites views all exist
One remaining manual view â "Websites â HTTPS Required" (filter: protocol = https) â needs creation in NocoDB UI in the ops_websites table. POST /views still broken.
Part 5 â Hazel runbook â v2.5 â
(8,649 chars) â Host Registry section added with node table, URL rules, gateway policy, and note about pike port ambiguity.
ops_run_reports row 27 logged â
'
Thatâs a solid rollout â youâve got the important pieces in place (host registry, normalized URLs, gateway endpoints, runbook update, and an ops_run_reports trail).
The remaining items are straightforward:
1) Create the one missing manual view
In Ops base â ops_websites, create:
View: âWebsites â HTTPS Requiredâ
- Filter:
protocol = https - Sort:
host_ref asc,port asc
This becomes Hazelâs quick âthings that must be TLSâ checklist.
2) Resolve the pike gateway port ambiguity before Hazel automates anything
Right now you have:
https://pike.tail39fdfd.ts.net:18888(actual_state=unknown)
âŚand a note that âpoppy runs on 18789â.
Do this once, manually:
- Try opening both endpoints (18888 and 18789) from a machine on Tailscale and see which one actually responds for the gateway.
- Then update ops-openclaw-gateway-pike to the correct port and set:
actual_state=uphealthcheck_url= that same base URL (so Hazel can validate it)
Until then, keep:
can_auto_restart=falsefor that service/endpoint (no automated remediation).
3) Next operational step: let Hazel run the daily maintenance loop
Now that the registries exist, Hazelâs daily run should start producing value immediately:
- Hosts Offline view
- Persistent websites health (especially NocoDB/Immich/Open-WebUI)
- Deprecated-but-still-up cleanup queue
- Port conflicts
If you want, paste Hazelâs first daily report here and Iâll sanity-check whether the fields/views are giving her enough signal (and whether anything should be promoted from âunknownâ to âsafe auto-restartâ).
Also, how do we manage cron jobs that happen every minute or 15 minutes or hour? Are those being tracked by Hazel?
Yes â the everyâminute / 15âminute / hourly jobs should be tracked, but you donât want Hazel (or NocoDB) drowning in 1,440 RunReport rows per day per job.
The right pattern is:
1) Track all scheduled jobs in ops_scheduled_jobs (Hazel-owned)
Every cron/LaunchAgent âjobâ gets a row with:
schedule(e.g.*/1 * * * *,*/15 * * * *, hourly, etc.)enabledfailure_policy- expected cadence (interval minutes) and health fields (below)
2) Use tiered logging (full RunReport vs lightweight heartbeat)
For hourly/daily/weekly jobs
- Do a full
ops_run_reportsrow every run (works great, low volume)
For everyâminute / everyâ15âminute jobs
- Donât write a full RunReport every time.
- Instead, the job updates its own
ops_scheduled_jobsrow fields:last_started_atlast_success_atlast_status(success/fail)consecutive_failureslast_error(short, if fail)
Only create an ops_run_reports row when:
- the job fails (blocked/fail), or
- a âsummary runâ happens (e.g. once per day Hazel emits a digest)
This keeps visibility high and noise low.
3) Hazelâs responsibility
Hazel should:
- own
ops_scheduled_jobsas the registry - ensure each frequent job updates
last_success_atreliably
- during the daily maintenance run, check for:
- stale jobs:
now - last_success_at > 2Ă interval - flapping jobs: consecutive_failures > threshold
- and then alert/remediate per failure_policy
- stale jobs:
Add one view to make this easy
âJobs â Stale / Failingâ
- Filter: enabled=true AND (
consecutive_failures >= 1 OR
last_success_at is empty OR
last_success_at older than (interval_minutes * 2)
)
If you want, I can write a Juniper prompt to extend ops_scheduled_jobs with the âhealth fieldsâ (last_success_at, interval_minutes, consecutive_failures, last_error) and update Hazelâs daily runbook to treat frequent jobs as heartbeat-updated rather than RunReport-per-run.
write a Juniper prompt to extend ops_scheduled_jobs with the âhealth fieldsâ (last_success_at, interval_minutes, consecutive_failures, last_error) and update Hazelâs daily runbook to treat frequent jobs as heartbeat-updated rather than RunReport-per-run.
You are Juniper. Task: extend ops_scheduled_jobs with health/heartbeat fields so Hazel can track frequent jobs (every minute / 15 min) without creating a full ops_run_reports row per run, and update Hazelâs runbook accordingly.
SAFETY / DEPRECATION
- Old ACT v2 protocols are deprecated; do not follow them.
- Additive changes only (no destructive edits).
- Do not paste tokens/secrets in outputs.
PART 1 â Extend ops_scheduled_jobs schema (Ops base â Ops Inventory â ops_scheduled_jobs)
Add these columns if missing:
Cadence / expectations
- interval_minutes (integer) // e.g. 1, 15, 60, 1440
- max_staleness_multiplier (decimal) // default 2.0 (stale if last_success_at older than 2Ă interval)
Heartbeat/health fields (updated by the job itself)
- last_started_at (datetime nullable)
- last_success_at (datetime nullable)
- last_failure_at (datetime nullable)
- last_status (select: success | fail | unknown)
- consecutive_failures (integer, default 0)
- last_error (long text nullable)
RunReport linkage (for low-frequency jobs or failures)
- last_run_report (link â ops_run_reports) // keep existing; use mostly for failures/summary runs
PART 2 â Define logging policy in the table (document in notes)
Update ops_scheduled_jobs.notes (or add a column logging_mode) with:
- logging_mode (select): full_runreport | heartbeat_only
Defaults:
- interval_minutes <= 15 â heartbeat_only
- interval_minutes >= 60 â full_runreport
(Allow overrides per job.)
PART 3 â Create key views (Ops base â Ops Views)
1) âJobs â Stale / Failingâ
Filter:
- enabled = true
- AND (
last_success_at is empty OR
last_status = fail OR
consecutive_failures >= 1 OR
last_success_at older than (interval_minutes * max_staleness_multiplier)
)
Sort:
- consecutive_failures desc
- last_success_at asc
(If NocoDB canât do the computed staleness filter, approximate with:
- last_success_at older than X hours for minute/15-min jobs,
and maintain separate views per cadence.)
2) âJobs â Frequent (Heartbeat Mode)â
Filter:
- enabled=true AND interval_minutes <= 15
Sort:
- job_name asc
3) âJobs â Full RunReport Modeâ
Filter:
- enabled=true AND interval_minutes >= 60
Sort:
- job_type asc, job_name asc
PART 4 â Update Hazel runbook (Ops base â agent_runbooks â Hazel)
Add a section: âFrequent Jobs: Heartbeat Mode (No RunReport per run)â
Include:
- For interval_minutes <= 15:
- The job should update ops_scheduled_jobs fields each run:
last_started_at, last_success_at, last_status, consecutive_failures, last_error
- Only create ops_run_reports when:
- a failure occurs, OR
- a daily summary is produced
- Hazel Daily Maintenance must:
- open âJobs â Stale / Failingâ
- act per failure_policy (alert_only / retry / restart_service / block)
- include a summary of job health in ops_run_reports daily digest
- Hazel must NOT allow heartbeat jobs to silently fail:
- if last_success_at is stale beyond 2Ă interval â treat as incident/blocker
PART 5 â Deliverable
- Post ops_run_reports (project=ops) summarizing:
- new columns added
- views created
- Hazel runbook updated
- next action: migrate existing frequent jobs to update these heartbeat fields
OK, can you write me updated daily and weekely prompts for juniper to set Hazels cadence and regular work. does hazel also work with scope tickets and task packets?
Yes â Hazel should still use the same structure (Scope Tickets + Task Packets). The difference is: her âscopeâ is usually an Ops view (âServices â Downâ, âJobs â Stale/Failingâ, âWebsites â Deprecated But Still Upâ), not a reel ID.
Think of Hazelâs daily/weekly as recurring Task Packets stored in ops_scheduled_jobs, and Juniper just triggers them.
Below are two updated Juniper prompts:
1) Juniper prompt â set Hazel cadence + create/upgrade the scheduled job rows
You are Juniper. Task: set Hazelâs cadence and recurring work by configuring ops_scheduled_jobs for Hazel Daily and Weekly Ops Maintenance, using the new health fields model.
WHERE
Ops base â ops_scheduled_jobs
A) Ensure these two jobs exist (create if missing)
1) job_name = hazel_daily_ops_maintenance
- job_type = daily
- schedule = daily 06:00 (or your chosen time)
- enabled = true
- interval_minutes = 1440
- logging_mode = full_runreport
- failure_policy = alert_only (default) or restart_service (only if safe)
- script_or_command_ref = âHazel Daily Ops Maintenance (see Hazel runbook)â
2) job_name = hazel_weekly_ops_maintenance
- job_type = weekly
- schedule = weekly (Sun 09:00) (or your chosen time)
- enabled = true
- interval_minutes = 10080
- logging_mode = full_runreport
- failure_policy = alert_only
- script_or_command_ref = âHazel Weekly Ops Maintenance (see Hazel runbook)â
B) Add/confirm âhealth fieldsâ on both jobs
- last_started_at, last_success_at, last_status, consecutive_failures, last_error
Initialize:
- last_status = unknown
- consecutive_failures = 0
C) Wire the canonical scope into the job notes
In each jobâs notes, specify the scope views Hazel must use:
- Services â Down or Unknown
- Jobs â Stale / Failing
- Websites â Persistent (Should Be Up)
- Websites â Deprecated But Still Up
- Websites â Port Conflicts
- Hosts â Offline
D) Update Hazel runbook to reference these jobs
Ops base â agent_runbooks â Hazel:
- add âCadenceâ section:
- daily: hazel_daily_ops_maintenance
- weekly: hazel_weekly_ops_maintenance
- include the tiny invocation prompts (below) so anyone can trigger manually.
DELIVERABLE
- Post ops_run_reports (project=ops) confirming schedules + job rows updated.
2) Juniper prompts â the actual daily + weekly âinvocationâ messages to Hazel
These are what Juniper (or your scheduler) sends to Hazel at runtime. Theyâre intentionally short; Hazel pulls the full procedure from her runbook.
Daily invocation prompt (send to Hazel)
Hazel: Run âDaily Ops Maintenanceâ now.
1) Rehydrate: Ops base â agent_runbooks â Hazel.
2) Create ops_run_reports row (project=ops) and heartbeat every 30â60 min.
3) Use canonical scope views:
- Services â Down or Unknown
- Jobs â Stale / Failing
- Websites â Persistent (Should Be Up)
- Websites â Deprecated But Still Up
- Websites â Port Conflicts
- Hosts â Offline
4) Apply safe remediation rules (bounded restarts only; no host reboot by default).
5) Close ops_run_reports with summary + links.
6) Update ops_scheduled_jobs row hazel_daily_ops_maintenance:
last_success_at/last_status/consecutive_failures/last_error + last_run_report link (if used).
Weekly invocation prompt (send to Hazel)
Hazel: Run âWeekly Ops Maintenanceâ now.
1) Rehydrate: Ops base â agent_runbooks â Hazel.
2) Create ops_run_reports row (project=ops) and heartbeat.
3) Reconcile inventory drift:
- ops_services / ops_websites / ops_launch_agents accuracy
- deprecated sites still up
- recurring failures from Jobs â Stale/Failing
4) Propose 1â3 runbook improvements (donât invent new systems).
5) Close ops_run_reports with weekly summary + links.
6) Update ops_scheduled_jobs hazel_weekly_ops_maintenance health fields.
If you want, I can also write the Hazel Daily/Weekly Task Packets in your Task Packet template format, so the scheduled job rows simply point to those packets as the canonical instructions.
yes please do, are those rewrites of the invocation prompts, or additive to them?
Theyâre additive, not replacements.
- Invocation prompts stay as the tiny ârun it nowâ trigger (good for schedulers/cron and for you to manually send).
- Task Packets become the canonical, reusable procedure (stored durably and linked from
ops_scheduled_jobs+ Hazel runbook).
You can optionally rewrite the invocation prompts to be even shorter (âRun Task Packet: Hazel Daily Ops Maintenanceâ), but the underlying concept is: invocation = trigger, task packet = instructions.
Below are the two Task Packets.
Hazel Daily Ops Maintenance â Task Packet
TASK PACKET (v1)
Title: Hazel â Daily Ops Maintenance (health + safe remediation + digest)
Project: ops
Assigned agent: Hazel
Runbook: Ops base â agent_runbooks â agent=Hazel (must read before starting)
1) Goal
Detect outages early, safely remediate what is safe, and produce a clear daily ops digest with links to inventory views.
2) Scope
IN SCOPE:
- Ops base tables/views:
- ops_services + âServices â Down or Unknownâ
- ops_websites + âWebsites â Persistent (Should Be Up)â
- ops_websites + âWebsites â Deprecated But Still Upâ
- ops_websites + âWebsites â Port Conflictsâ
- ops_hosts + âHosts â Offlineâ
- ops_scheduled_jobs + âJobs â Stale / Failingâ
OUT OF SCOPE:
- Any destructive changes (deleting jobs/sites) without explicit approval
- Host reboots (unless explicitly permitted by runbook and can_auto_reboot_host=true)
- Guessing restart commands when restart_ref is missing
3) Quota / Stop conditions
- Time limit: 60 minutes max
- Stop early if: DB unavailable, cannot access views, widespread outage detected (escalate)
4) Inputs
- The canonical views listed above
- Current Hazel runbook policies (safe restart rules, retries)
5) Writeback targets
- ops_run_reports: one row for the run (mandatory)
- ops_services: update last_seen_healthy_at / last_restart_at / notes as applicable
- ops_websites: update actual_state / last_seen_up_at as applicable
- ops_scheduled_jobs: update health fields for the daily job row:
- last_started_at, last_success_at, last_status, consecutive_failures, last_error
- last_run_report (link to ops_run_reports) if you use it
6) Output / Done criteria
- [ ] ops_run_reports row created at start, closed done/blocked with summary + links
- [ ] All âDown/Unknownâ services triaged (restart only if allowed)
- [ ] Deprecated-but-still-up sites flagged (shutdown only if shutdown_ref exists; else block)
- [ ] Port conflicts flagged with clear next steps
- [ ] Jobs stale/failing flagged with next steps
- [ ] ops_scheduled_jobs health fields updated for daily job
7) Safety / privacy constraints
- Never paste secrets/tokens in summaries
- Never self-kill slow work; checkpoint + ask
- Max restart attempts per service per run: 2 (then block)
8) RunReport parameters
- ops_run_reports.project = ops
- progress_unit = checks
- progress_total = 6
- Heartbeat = every 30â60 minutes (and after major remediation)
9) Blocker protocol
If blocked:
- ops_run_reports.status=blocked
- blocker = what prevented safe progress
- next_step = exact action required (e.g., âadd restart_ref for service Xâ)
10) Review request
Include links in the run report to:
- Services â Down or Unknown
- Websites â Deprecated But Still Up
- Jobs â Stale / Failing
Hazel Weekly Ops Maintenance â Task Packet
TASK PACKET (v1)
Title: Hazel â Weekly Ops Maintenance (drift cleanup + reliability improvements)
Project: ops
Assigned agent: Hazel
Runbook: Ops base â agent_runbooks â agent=Hazel
1) Goal
Reduce drift: ensure inventory matches reality, deprecated sites are turned off, recurring failures produce runbook fixes, and ops hygiene improves week over week.
2) Scope
IN SCOPE:
- Reconcile ops_services / ops_websites / ops_launch_agents / ops_hosts accuracy
- Review repeated incidents from ops_run_reports and job failures from ops_scheduled_jobs
- Propose runbook edits (explicit change requests) to prevent recurring failures
OUT OF SCOPE:
- Major migrations / refactors
- Deleting lots of things without explicit approval
3) Quota / Stop conditions
- Time limit: 90 minutes max
- Stop early if: you discover a high-severity outage (switch to incident mode)
4) Inputs
- Ops Views:
- Websites â Deprecated But Still Up
- Websites â Port Conflicts
- Jobs â Stale / Failing
- Hosts â Offline
- Recent ops_run_reports (last 7 days)
5) Writeback targets
- ops_run_reports weekly summary (mandatory)
- ops_scheduled_jobs weekly job health fields updated (last_success_at etc.)
- Update inventory tables when you have high confidence:
- mark deprecated hosts/sites
- fill missing runtime/startup_ref/shutdown_ref when known
- Create explicit runbook_change_request text (in run report or a dedicated field/table)
6) Output / Done criteria
- [ ] Weekly ops_run_reports row closed with:
- top recurring failures
- deprecated sites still up (with plan)
- port conflicts (with plan)
- 1â3 prioritized runbook changes
- [ ] Inventory drift reduced (fewer âunknownâ/missing refs)
- [ ] Weekly job health fields updated
7) Safety
- Do not guess shutdown/start commands; only use refs
- No host reboot by default
8) RunReport parameters
- ops_run_reports.project = ops
- progress_unit = checks
- progress_total = 5
- Heartbeat = every 30â60 minutes
9) Blocker protocol
If blocked, write:
- blocker + next_step (e.g. âneed approval to deprecate site Xâ)
10) Review request
Link to:
- Websites â Deprecated But Still Up
- Jobs â Stale / Failing
- Last 7 days ops_run_reports
Optional simplification (recommended)
Once these Task Packets exist durably (runbook or a âtask_packetsâ table), you can rewrite the invocation prompts to just:
- âHazel: run Task Packet âHazel â Daily Ops Maintenanceââ
- âHazel: run Task Packet âHazel â Weekly Ops Maintenanceââ
âŚbut thatâs purely a convenience. The packets are additive and compatible with your current invocation prompts.
the invocation prompts are things I need to cut and paste when I need them, or are they juniper's?
Theyâre both, but the âcleanâ setup is:
Canonical owner = Juniper (stored, not copy/paste)
Juniper should store the canonical invocation prompts in durable places so you donât have to hunt for them:
ops_scheduled_jobs.script_or_command_ref(or aprompt_textfield)- Hazelâs runbook (âInvocation promptsâ section)
That way:
- your scheduler can use them
- Juniper can trigger them
- you always have one up-to-date source of truth
Manual use = you (copy/paste when you want to trigger a run)
Any time you want to run Hazel right now, you can paste the invocation prompt (or even just send âHazel: run Daily Ops Maintenance per your runbookâ). The long logic is in the runbook/task packet, not in the trigger.
So: Juniper owns maintaining them; you can use them ad-hoc.
OK can you write the prompt for juniper for me to prepend to the invocation prompts, and the prompt to prepend to the task packets?
Here are two prepend blocks Juniper can paste on top of:
- every invocation prompt (the tiny ârun it nowâ triggers)
- every Task Packet (the full instructions)
Theyâre intentionally short, but they enforce the system-wide rules we agreed on.
1) Prepend block for Invocation Prompts (Juniper-owned trigger header)
[INVOCATION HEADER â READ FIRST]
This message is an INVOCATION (a trigger), not a full spec.
Do NOT improvise policy or invent new procedures.
MANDATORY BEFORE EXECUTION:
1) Rehydrate from Ops base â agent_runbooks â your agent row (canonical spec).
2) Create an ops_run_reports row BEFORE starting work (status=running + heartbeat).
3) Follow the referenced Task Packet / runbook procedure exactly.
4) If any required table/view/schema is missing â mark ops_run_reports=blocked with next_step and STOP.
5) Never self-kill slow work. Checkpoint + ask if suspected hang.
6) Never paste secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / capability scoring protocols are deprecated.
- Trello is backlog only; outputs/visibility go to ops_run_reports + project DB tables.
Use it like:
[INVOCATION HEADER âŚ]
Hazel: Run âDaily Ops Maintenanceâ nowâŚ
2) Prepend block for Task Packets (full instruction header)
[TASK PACKET HEADER â SYSTEM RULES]
SOURCE OF TRUTH:
- Runbook (Ops base â agent_runbooks â your row) is the canonical spec.
- ops_run_reports is the canonical execution log.
- Project DB tables are the canonical artifacts (not hidden files).
- Trello is planning/backlog only.
MANDATORY EXECUTION CONTRACT:
- Create ops_run_reports row before work; heartbeat every 30â60 min (+ per-unit thresholds if defined).
- Work in bounded batches (quota + time limit). Stop when quota/time limit hit.
- If uncertain: route to needs_review / Review Inbox; do not guess.
- Never self-kill slow work; checkpoint + ask if suspected hang.
- Never include secrets/tokens/credentials in summaries or artifacts.
DEPRECATION:
- Old ACT v2 / 3-gate capability protocol is deprecated.
- Judy audits compliance + evidence presence only (not âgeneral intelligenceâ).
DONE CRITERIA (always):
- ops_run_reports closed done/blocked with counts + links to outputs/views.
- Canonical tables updated as specified.
If you want, I can also give you a 1â2 line âsuper-short invocationâ version (âRun Task Packet X per runbook; log to ops_run_reportsâ) that works well once everything is stable.
are these are stored in the runbooks of juniper and hazel?
They should be, but the best practice is:
Canonical storage: Ops base â one shared âtemplates/contractâ place
So you donât have to keep them duplicated across multiple runbooks.
Two good options:
- Add a âGlobal Contract + Templatesâ section to Juniperâs runbook (simplest)
- Juniper is the system maintainer, so she owns the canonical copy.
- Everyone else references it.
- Create a tiny table in the Ops base like
ops_templates(cleanest)
Rows like:
invocation_headertask_packet_header- (later)
scope_ticket_template, etc.
Do Hazel/Juniper both need them?
- Juniper: yes â sheâs the one generating/standardizing packets and invocations.
- Hazel: optional. Hazel mostly executes jobs; she doesnât need to author packets. If you want consistency, you can paste the headers into Hazelâs runbook under a âReference templatesâ section, but itâs not required.
If you want, I can write the Juniper prompt to add these headers into:
- Juniperâs runbook (canonical section), and
- optionally create
ops_templatesin the Ops base.
I am getting confused on what to direct where. so before we write any more prompts, does ops_templates need to have customized templates for each agent?
No â ops_templates should stay mostly generic, otherwise it becomes another place you have to remember âwhich version is correct.â
Hereâs the clean split that keeps you sane:
What goes where (simple rule)
ops_templates (global, reusable, not agent-specific)
Store only council-wide templates that never change per agent, like:
invocation_headertask_packet_headerscope_ticket_template- maybe
deprecation_notice
These are âformat + rulesâ.
agent_runbooks (agent-specific, always customized)
Each agentâs runbook holds the real operating procedure:
- Hazel: daily/weekly ops checks, inventory tables, safe remediation
- Olive: reels queue views, segment rules, writeback tables
- Rosemary: watch enrichment pipeline, citations, conflicts
- Holly: property doc indexing rules, alias scoring, review inbox
- Judy: audit rubric + compliance_audits table
These are âhow to do the workâ.
ops_scheduled_jobs (schedules + pointers)
Holds:
- cadence (every minute / daily / weekly)
- enabled
- logging mode (heartbeat vs full runreport)
- pointer to the runbook / task packet name
If you want convenience later
You can add optional agent-specific templates as separate entries (e.g. task_packet_hazel_daily), but donât do it until you feel real pain. Start with just the 3â5 global templates.
If you want, I can write a one-paragraph âcheat sheetâ you pin in Juniperâs runbook: âWhen youâre confused, check this map.â
OK, please give me the comprehensive prompt for juniper that implements council-wide templates, removes any redundancies from the exisiting agent instructions/runbooks, and canonnizes the three main doccuments for each job, the invocation, the Scope Ticket and the Task Packet (are there more?) and creates the ops_templates that everyone can and should reference. Can juniper also scan each agewnts runbook to make sure they are consistent and don't have redundant sections? or is that too much scope for one prompt?
Youâre asking for the right thing: one canonical template layer, so every job looks the same and runbooks stop duplicating the same boilerplate.
Is it too much scope for one prompt? It can be if Juniper tries to perfect everything in one pass. But itâs totally doable as one prompt with phased deliverables + a hard timebox.
Also: yes, thereâs one more âdocumentâ type you should treat as first-class:
- Runbook (per-agent, persistent spec)
So the set is really:
- Invocation (trigger)
- Scope Ticket (one variable + quota)
- Task Packet (full instructions)
- Runbook (durable per-agent operating manual)
âŚand the runtime log isops_run_reports.
Below is a comprehensive Juniper prompt that does exactly what you asked, but stays bounded.
Comprehensive prompt for Juniper (templates + dedupe + runbook consistency scan)
You are Juniper. Task: implement council-wide canonical templates, reduce redundancy across agent instructions/runbooks, and standardize the three main job documents (Invocation, Scope Ticket, Task Packet). Timebox this to 90 minutes and produce a clear âwhat changedâ report.
GLOBAL DEPRECATION (must enforce)
- Old ACT v2 / 3-gate capability protocol is deprecated.
- Trello is backlog/planning only; outputs + visibility are ops_run_reports + project DB tables.
- Never paste tokens/secrets into runbooks/templates.
GOAL
Create a single source of truth for:
- Invocation header template
- Scope Ticket template
- Task Packet header template
âŚand refactor agent runbooks to reference these templates instead of duplicating boilerplate.
PHASE 0 â Safety + snapshot (5 minutes)
- Before edits: copy current runbook text for each agent into a âbackupâ note (or add a short âPrevious version saved on <date>â section).
- Do not delete content youâre unsure about; prefer âmove to backup sectionâ if needed.
PHASE 1 â Create ops_templates (canonical templates) (20 minutes)
In Ops base, create table: ops_templates
Columns:
- template_name (text unique)
- template_type (select: invocation_header | scope_ticket_template | task_packet_header | other)
- version (text)
- template_text (long text)
- last_updated_at (datetime)
- notes (long text nullable)
Seed these rows (canonical):
1) template_name = invocation_header_v1
template_type = invocation_header
template_text = (paste the standard Invocation Header we agreed on)
2) template_name = scope_ticket_template_v1
template_type = scope_ticket_template
template_text = (paste the standard Scope Ticket template)
3) template_name = task_packet_header_v1
template_type = task_packet_header
template_text = (paste the standard Task Packet Header we agreed on)
OPTIONAL (recommended):
4) runreport_contract_v1 (the RunReport + Heartbeat Contract)
5) deprecation_notice_v1 (Trello-only + old ACT deprecated)
PHASE 2 â Canonize âthe three job documentsâ (what they are) (10 minutes)
Write a short âCouncil Standardâ note (in Juniper runbook or ops_templates as council_standard_v1):
- Invocation = Trigger only. Must begin with invocation_header template and point to runbook/task packet.
- Scope Ticket = One scope variable + quota + stop rules.
- Task Packet = Full instructions; must begin with task_packet_header template.
- Runbook = Per-agent durable operating manual.
- ops_run_reports = Runtime log (every run).
PHASE 3 â Remove redundancies from agent runbooks (scan + refactor) (40 minutes)
For each agent runbook (Hazel, Olive, Rosemary, Holly, Judy, Juniper):
1) Scan for duplicated boilerplate sections:
- repeated RunReport contract blocks
- repeated deprecation notices
- repeated task packet header text
2) Refactor as:
- Keep ONLY agent-specific procedures and project-specific rules in the runbook.
- Replace boilerplate with a short reference line:
âSee Ops base â ops_templates â invocation_header_v1 / task_packet_header_v1 / runreport_contract_v1.â
3) Ensure each runbook still contains:
- the agentâs mission
- canonical tables/views it uses
- batch defaults + safety rules
- where to write outputs
4) Do NOT remove essential agent-specific rules (e.g., Holly alias scoring, Rosemary evidence-first).
PHASE 4 â Consistency check (15 minutes)
Run a consistency audit across runbooks:
- confirm every runbook:
- references ops_templates
- uses ops_run_reports as the execution log
- does not reference the deprecated ACT protocol
- does not claim Trello is the output
- record any remaining inconsistencies in a short âRunbook Cleanup TODOâ list (donât chase perfection).
DELIVERABLES (must produce)
1) ops_templates table exists with the three canonical templates (and optional 2).
2) Each agent runbook updated to reference templates (reduced boilerplate) and remains coherent.
3) Post one ops_run_reports row (project=ops) summarizing:
- templates created (names/versions)
- runbooks updated (which agents)
- any remaining TODOs (if timeboxed out)
4) Never include tokens/IDs/secrets in any output.
If you want, I can also paste the exact template_text for the three ops_templates rows (invocation header, scope ticket template, task packet header) in one chunk so Juniper doesnât have to hunt them down from chat history.
yes, write it as a prompt for me to give juniper.
Juniper â Council Templates Canonicalization + Runbook Dedupe (90 min timebox)
You are Juniper. Task: implement council-wide canonical templates in Ops base (`ops_templates`), refactor agent runbooks to reference these templates (remove redundant boilerplate), and run a consistency scan across all runbooks.
CRITICAL SAFETY + DEPRECATION
- Do NOT paste or log tokens/IDs/secrets anywhere in templates or runbooks.
- Old ACT v2 / 3-gate capability protocol is deprecated. Do not follow or reference it.
- Trello is backlog/planning only; outputs + visibility are ops_run_reports + project DB tables.
PHASE 0 â Snapshot (quick safety)
Before edits, capture the current runbook_text for each agent in a âbackupâ section (or a separate note/table) so we can roll back.
PHASE 1 â Create ops_templates (Ops base)
In NocoDB Ops base create a table: ops_templates
Columns:
- template_name (text unique)
- template_type (select: invocation_header | scope_ticket_template | task_packet_header | contract | deprecation | other)
- version (text)
- template_text (long text)
- last_updated_at (datetime)
- notes (long text nullable)
Then create these rows EXACTLY (copy/paste template_text):
----------------------------------------
Template 1: invocation_header_v1
template_type: invocation_header
version: v1
template_text:
[INVOCATION HEADER â READ FIRST]
This message is an INVOCATION (a trigger), not a full spec.
Do NOT improvise policy or invent new procedures.
MANDATORY BEFORE EXECUTION:
1) Rehydrate from Ops base â agent_runbooks â your agent row (canonical spec).
2) Create an ops_run_reports row BEFORE starting work (status=running + heartbeat).
3) Follow the referenced Task Packet / runbook procedure exactly.
4) If any required table/view/schema is missing â mark ops_run_reports=blocked with next_step and STOP.
5) Never self-kill slow work. Checkpoint + ask if suspected hang.
6) Never paste secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / capability scoring protocols are deprecated.
- Trello is backlog only; outputs/visibility go to ops_run_reports + project DB tables.
----------------------------------------
----------------------------------------
Template 2: scope_ticket_template_v1
template_type: scope_ticket_template
version: v1
template_text:
SCOPE TICKET (v1) â template
Title:
Project: (reels | watch | property | ops)
Agent:
Related Task Packet:
Single Scope Variable (the ONE thing that makes this run unique):
- <SCOPE_VARIABLE_NAME> = <VALUE>
Selection rule (how to choose items within scope):
- (e.g., âonly this reel_media_asset_idâ, âtop 10 watches with links_count=0â, âonly files under this folderâ)
Quota / Stop:
- Quota:
- Time limit:
- Stop early if:
Inputs (exact pointers):
- NocoDB view(s) to start from:
- File paths / IDs / filters:
Writeback targets (must be explicit):
- Tables to write:
- Views/artifacts that should update:
Success criteria (quick):
- ops_run_reports row exists + heartbeat
- quota reached OR clean stop
- run closed done/blocked with links
Notes / Safety:
- (any special constraints)
----------------------------------------
----------------------------------------
Template 3: task_packet_header_v1
template_type: task_packet_header
version: v1
template_text:
[TASK PACKET HEADER â SYSTEM RULES]
SOURCE OF TRUTH:
- Runbook (Ops base â agent_runbooks â your row) is the canonical spec.
- ops_run_reports is the canonical execution log.
- Project DB tables are the canonical artifacts (not hidden files).
- Trello is planning/backlog only.
MANDATORY EXECUTION CONTRACT:
- Create ops_run_reports row before work; heartbeat every 30â60 min (+ per-unit thresholds if defined).
- Work in bounded batches (quota + time limit). Stop when quota/time limit hit.
- If uncertain: route to needs_review / Review Inbox; do not guess.
- Never self-kill slow work; checkpoint + ask if suspected hang.
- Never include secrets/tokens/credentials in summaries or artifacts.
DEPRECATION:
- Old ACT v2 / 3-gate capability protocol is deprecated.
- Judy audits compliance + evidence presence only (not âgeneral intelligenceâ).
DONE CRITERIA (always):
- ops_run_reports closed done/blocked with counts + links to outputs/views.
- Canonical tables updated as specified.
----------------------------------------
OPTIONAL (recommended) â add these too:
----------------------------------------
Template 4: runreport_contract_v1
template_type: contract
version: v1
template_text:
RUNREPORT + HEARTBEAT CONTRACT (MANDATORY)
On every job run:
1) Create a row in ops_run_reports before starting work:
project=<watch|reels|property|ops>, agent=<your name>, status=running,
started_at=now, last_heartbeat_at=now, progress_unit, progress_total.
2) Heartbeat:
Update ops_run_reports.last_heartbeat_at + progress at least every 30â60 minutes
AND at each per-unit threshold (e.g., every 50 docs/scenes or per watch).
3) Completion:
Set status=done (or blocked) with a concise summary and links to artifacts/views.
Never include secrets in summaries.
4) If you cannot access the DB / runbook / ops_run_reports:
Do not proceed. Mark blocked with next_step and stop.
5) Never self-kill slow work. If you suspect a hang, checkpoint + ask.
----------------------------------------
----------------------------------------
Template 5: deprecation_notice_v1
template_type: deprecation
version: v1
template_text:
DEPRECATION NOTICE (v1) â Effective immediately
Older protocols are deprecated and must NOT be followed if they conflict with runbooks/templates:
1) âTrello is the output / only signalâ â DEPRECATED
- Trello is planning/backlog only.
- Execution visibility and outputs must be recorded in:
- ops_run_reports (run logging + heartbeats)
- project database tables (artifacts + structured results)
2) âACT protocol grades general capabilityâ â DEPRECATED
- Judy is used for reliability/compliance audits only (run reports, batching, evidence, review routing, safety).
3) âRely on chat contextâ â DEPRECATED
- Chat history is not the source of truth.
- Rehydrate from runbooks + ops tables on every run/reset.
If any memory/instruction conflicts with this notice, treat the older instruction as deprecated.
----------------------------------------
PHASE 2 â Canonize the document model (Council Standard)
In Juniper runbook (or as a council_standard_v1 template), record:
- Invocation = trigger only; must begin with invocation_header_v1; points to runbook/task packet.
- Scope Ticket = one scope variable + quota.
- Task Packet = full instructions; must include task_packet_header_v1.
- Runbook = per-agent durable spec.
- ops_run_reports = runtime log.
PHASE 3 â Runbook dedupe + consistency scan (all agents)
For each agent runbook (Juniper, Hazel, Olive, Rosemary, Holly, Judy):
- Remove duplicated boilerplate (contracts/headers/deprecation blocks) and replace with references:
âSee Ops base â ops_templates â <template_name>â
- Ensure each runbook still contains agent-specific content:
- mission + scope
- canonical tables/views
- batch defaults + safety rules
- where outputs go
- Ensure no runbook references old ACT v2 âcapability scoringâ.
- Ensure none claim âTrello is the outputâ.
PHASE 4 â Deliverables
- ops_templates populated with the templates above.
- Each agent runbook references templates and is free of redundant boilerplate.
- Post 1 ops_run_reports row (project=ops) summarizing:
templates created, runbooks updated, remaining TODOs (if any).
If you want, I can also provide a one-paragraph âcheat sheetâ for Juniperâs runbook: âWhen confused, hereâs what lives in Trello vs Ops base vs project bases.â
yes please write it as an addendum to the prompt you just wrote
ADDENDUM â Council âWhere Does This Live?â Cheat Sheet (append to the Juniper prompt)
After PHASE 2 (Council Standard), add this step:
PHASE 2.5 â Create Council Cheat Sheet (reduce operator confusion)
Create a short âsystem mapâ that answers âwhere do I put what?â and âwhat do I message whom?â
Option A (recommended): store it as an ops_templates row so everyone can reference it:
- template_name = council_cheatsheet_v1
- template_type = other
- version = v1
- template_text (paste exactly below)
Also paste the same cheat sheet into Juniperâs runbook under a heading:
âCouncil System Map (Cheat Sheet)â
Cheat sheet text to paste:
========================
COUNCIL SYSTEM MAP (v1) â Where things live
========================
Trello (Backlog/Planning)
- What: ideas, priorities, milestones, âwhat to do nextâ
- Contains: Scope Tickets + links to Task Packets / run reports
- Does NOT contain: actual outputs/artifacts
Ops base (Execution Visibility + Persistence)
1) ops_run_reports
- What: runtime log of every run (start/heartbeat/done/blocked)
- Used for: âwhat happened today?â, âwas it blocked overnight?â
2) agent_runbooks
- What: durable agent operating manuals (how to do the work)
- Used for: context reset survival (agents rehydrate from here)
3) ops_templates
- What: council-wide templates and boilerplate
- invocation_header_v1
- scope_ticket_template_v1
- task_packet_header_v1
- runreport_contract_v1 (optional)
- deprecation_notice_v1 (optional)
- Used for: consistent formatting; avoids duplication across runbooks
4) Ops Inventory tables
- ops_hosts: machines + tailscale identifiers
- ops_services: services + health/restart refs
- ops_websites: URLs/ports + persistence + deprecated flags
- ops_scheduled_jobs: cadence + job health fields + last run report link
- ops_launch_agents: launchd inventory
- compliance_audits: Judyâs audit results (one row per audited run)
Project bases (Artifacts/Data)
- Reels DB: scenes/segments/tags/face frames + reel processing status
- Watch DB: searches/sources/links/conflicts/specs/valuations
- Property DB: documents/vendors/contracts/issues/maintenance + review inbox
The 3 job documents (standard)
1) Invocation
- Trigger only (begins with invocation_header_v1)
- Points to runbook + task packet
2) Scope Ticket
- One scope variable + quota + stop conditions
3) Task Packet
- Full instructions (begins with task_packet_header_v1)
Golden rule
- If itâs âwhat happened / is it stuck?â â ops_run_reports
- If itâs âhow do we do this reliably?â â agent_runbooks
- If itâs âwhat are the rules/templates?â â ops_templates
- If itâs âwhat did we produce?â â project base tables
- If itâs âwhat should we do next?â â Trello/backlog
========================
END CHEAT SHEET
========================
I know you already wrote these for me, but can you now give the invocation header/scope ticket/taskpacket for Hazels Daily and weekly jobs all together to issue to Juniper?
Below is a single bundle you can hand to Juniper: for Hazel Daily Ops Maintenance and Hazel Weekly Ops Maintenance, each includes:
- Invocation (with Invocation Header)
- Scope Ticket (one scope variable + quota)
- Task Packet (with Task Packet Header)
You can paste these into whatever Juniper uses (runbooks / ops_scheduled_jobs refs / docs).
HAZEL â DAILY OPS MAINTENANCE
1) Invocation (Daily)
[INVOCATION HEADER â READ FIRST]
This message is an INVOCATION (a trigger), not a full spec.
Do NOT improvise policy or invent new procedures.
MANDATORY BEFORE EXECUTION:
1) Rehydrate from Ops base â agent_runbooks â your agent row (canonical spec).
2) Create an ops_run_reports row BEFORE starting work (status=running + heartbeat).
3) Follow the referenced Task Packet / runbook procedure exactly.
4) If any required table/view/schema is missing â mark ops_run_reports=blocked with next_step and STOP.
5) Never self-kill slow work. Checkpoint + ask if suspected hang.
6) Never paste secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / capability scoring protocols are deprecated.
- Trello is backlog only; outputs/visibility go to ops_run_reports + project DB tables.
Hazel: Run âDaily Ops Maintenanceâ now using:
- Scope Ticket: HAZEL_DAILY_SCOPE_TICKET_v1
- Task Packet: HAZEL_DAILY_TASK_PACKET_v1
After completion:
- Close ops_run_reports with counts + links
- Update ops_scheduled_jobs row hazel_daily_ops_maintenance health fields (last_success_at, last_status, consecutive_failures, last_error)
2) Scope Ticket (Daily)
SCOPE TICKET (v1)
Title: Hazel â Daily Ops Maintenance (health + safe remediation + digest)
Project: ops
Agent: Hazel
Related Task Packet: HAZEL_DAILY_TASK_PACKET_v1
Single Scope Variable:
- job_name = hazel_daily_ops_maintenance
Selection rule:
- Use the canonical Ops views as scope (not ad-hoc exploration):
Services â Down or Unknown
Jobs â Stale / Failing
Websites â Persistent (Should Be Up)
Websites â Deprecated But Still Up
Websites â Port Conflicts
Hosts â Offline
Quota / Stop:
- Time limit: 60 minutes
- Stop early if: DB unavailable, cannot access views, widespread outage detected
Inputs:
- Ops base tables + views listed above
Writeback targets:
- ops_run_reports (run log)
- ops_services / ops_websites (health timestamps / state)
- ops_scheduled_jobs health fields for this job
Success criteria:
- Run report exists + heartbeat
- Safe remediation attempted only where allowed
- Run closed done/blocked with links and next steps
3) Task Packet (Daily)
[TASK PACKET HEADER â SYSTEM RULES]
SOURCE OF TRUTH:
- Runbook (Ops base â agent_runbooks â your row) is the canonical spec.
- ops_run_reports is the canonical execution log.
- Ops inventory tables are canonical (ops_hosts/services/websites/scheduled_jobs/launch_agents).
- Trello is planning/backlog only.
MANDATORY EXECUTION CONTRACT:
- Create ops_run_reports row before work; heartbeat every 30â60 min.
- Work in bounded batches (quota + time limit). Stop when limit hit.
- If uncertain: block; do not guess shutdown/restart steps.
- Never self-kill slow work; checkpoint + ask if suspected hang.
- Never include secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / 3-gate capability protocol is deprecated.
DONE CRITERIA:
- ops_run_reports closed done/blocked with counts + links to relevant views.
TASK PACKET (v1)
Title: HAZEL_DAILY_TASK_PACKET_v1 â Daily Ops Maintenance
Project: ops
Assigned agent: Hazel
Runbook: Ops base â agent_runbooks â Hazel
1) Goal
Detect outages early, safely remediate what is safe, and publish a daily ops digest with links.
2) Scope (canonical views)
- Services â Down or Unknown
- Jobs â Stale / Failing
- Websites â Persistent (Should Be Up)
- Websites â Deprecated But Still Up
- Websites â Port Conflicts
- Hosts â Offline
3) Quota / Stop conditions
- 60 minutes max.
- Stop early and mark blocked if: missing restart_ref/shutdown_ref for required action, or DB/views inaccessible.
4) Writeback
- ops_run_reports (mandatory)
- ops_services: update last_seen_healthy_at / last_restart_at / notes
- ops_websites: update actual_state / last_seen_up_at as applicable
- ops_scheduled_jobs (hazel_daily_ops_maintenance): last_started_at, last_success_at, last_status, consecutive_failures, last_error, (optional) last_run_report link
5) Safe remediation policy
- Restart only if ops_services.can_auto_restart=true AND restart_ref is defined/verified.
- Max 2 restart attempts per service per run.
- Never reboot host automatically (unless explicitly allowed by runbook; default false).
- For deprecated-but-still-up sites: only shut down if shutdown_ref exists; otherwise block with next_step.
6) Closeout
- Close ops_run_reports with:
- counts (services checked, restarts attempted, recovered, still down)
- port conflicts found
- deprecated sites still up
- jobs stale/failing
- links to the canonical views
HAZEL â WEEKLY OPS MAINTENANCE
1) Invocation (Weekly)
[INVOCATION HEADER â READ FIRST]
This message is an INVOCATION (a trigger), not a full spec.
Do NOT improvise policy or invent new procedures.
MANDATORY BEFORE EXECUTION:
1) Rehydrate from Ops base â agent_runbooks â your agent row (canonical spec).
2) Create an ops_run_reports row BEFORE starting work (status=running + heartbeat).
3) Follow the referenced Task Packet / runbook procedure exactly.
4) If any required table/view/schema is missing â mark ops_run_reports=blocked with next_step and STOP.
5) Never self-kill slow work. Checkpoint + ask if suspected hang.
6) Never paste secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / capability scoring protocols are deprecated.
- Trello is backlog only; outputs/visibility go to ops_run_reports + project DB tables.
Hazel: Run âWeekly Ops Maintenanceâ now using:
- Scope Ticket: HAZEL_WEEKLY_SCOPE_TICKET_v1
- Task Packet: HAZEL_WEEKLY_TASK_PACKET_v1
After completion:
- Close ops_run_reports with weekly summary + 1â3 runbook change requests
- Update ops_scheduled_jobs row hazel_weekly_ops_maintenance health fields
2) Scope Ticket (Weekly)
SCOPE TICKET (v1)
Title: Hazel â Weekly Ops Maintenance (drift cleanup + reliability improvements)
Project: ops
Agent: Hazel
Related Task Packet: HAZEL_WEEKLY_TASK_PACKET_v1
Single Scope Variable:
- job_name = hazel_weekly_ops_maintenance
Selection rule:
- Use the canonical Ops inventory + last 7 days of ops_run_reports as scope.
Quota / Stop:
- Time limit: 90 minutes
- Stop early if: high severity outage discovered (switch to incident/blocker mode)
Inputs:
- ops_hosts, ops_services, ops_websites, ops_launch_agents, ops_scheduled_jobs
- last 7 days ops_run_reports
- compliance_audits (optional signal)
Writeback targets:
- ops_run_reports (weekly summary)
- inventory tables (only high-confidence corrections)
- runbook_change_request text (explicit)
- ops_scheduled_jobs health fields for this job
Success criteria:
- Weekly summary exists with links + prioritized fixes
- Drift reduced (fewer unknowns, fewer deprecated-but-up)
3) Task Packet (Weekly)
[TASK PACKET HEADER â SYSTEM RULES]
SOURCE OF TRUTH:
- Runbook (Ops base â agent_runbooks â your row) is the canonical spec.
- ops_run_reports is the canonical execution log.
- Ops inventory tables are canonical.
- Trello is planning/backlog only.
MANDATORY EXECUTION CONTRACT:
- Create ops_run_reports row before work; heartbeat every 30â60 min.
- Work in bounded batches (quota + time limit). Stop when limit hit.
- If uncertain: block; do not guess shutdown/restart steps.
- Never self-kill slow work; checkpoint + ask.
- Never include secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / 3-gate capability protocol is deprecated.
DONE CRITERIA:
- ops_run_reports closed done/blocked with clear fixes + links.
TASK PACKET (v1)
Title: HAZEL_WEEKLY_TASK_PACKET_v1 â Weekly Ops Maintenance
Project: ops
Assigned agent: Hazel
Runbook: Ops base â agent_runbooks â Hazel
1) Goal
Reduce drift and improve reliability: inventory reconciliation, deprecated cleanup planning, repeated failure analysis, runbook improvements.
2) Scope
- Ops inventory tables: ops_hosts/services/websites/launch_agents/scheduled_jobs
- Ops views:
- Websites â Deprecated But Still Up
- Websites â Port Conflicts
- Jobs â Stale / Failing
- Hosts â Offline
- Last 7 days ops_run_reports
3) Quota / Stop conditions
- 90 minutes max.
- Stop early if severe outage discovered; mark blocked with next steps.
4) Writeback
- ops_run_reports weekly summary (mandatory)
- ops_scheduled_jobs (hazel_weekly_ops_maintenance) health fields updated
- inventory tables updated only when high confidence (mark deprecated entries, fill missing refs)
- 1â3 explicit runbook_change_requests
5) Safety
- No mass deletions. Prefer: mark deprecated + desired_state=down.
- No host reboot by default.
- No guessing restart/shutdown steps.
6) Closeout
- Weekly ops_run_reports must include:
- top recurring failures
- top 3 reliability fixes
- list of deprecated-but-up sites (with plan)
- port conflicts (with plan)
- links to the key views
If you want, I can also provide the exact minimal âsuper-shortâ invocation versions that just say âRun Task Packet X per runbookâ (useful once Juniper stores these in ops_templates / runbooks).
Do I need to update Juniper with with a complete ops template package like this one for hazel for each of the other agents?
You donât need to put a full âops template packageâ (invocation + scope + task) into ops_templates for every agent.
The clean rule
-
ops_templates= council-wide boilerplate only
(invocation header, scope ticket template, task packet header, runreport contract, deprecation notice, cheat sheet) -
Per-agent âpackagesâ live in the agentâs runbook or as job definitions in
ops_scheduled_jobs
(because theyâre not templates â theyâre canonical jobs)
When you do want a full package per agent
Only for recurring, scheduler-driven jobs, where you want one canonical version that never drifts:
- Hazel daily/weekly (yes)
- Rosemary nightly âimport searches â promote links â open conflictsâ (probably yes)
- Olive ongoing reel processing (yes, once stable)
- Holly periodic indexing batches (maybe, if you schedule it)
For ad-hoc work, you can just use:
- scope ticket + task packet generated as needed, referencing the runbook.
If you want one extra table (optional, reduces clutter)
Instead of stuffing per-agent jobs into runbooks, create:
ops_job_packets(job_name, agent, invocation_text, scope_ticket_text, task_packet_text, version, last_updated)
Thatâs the right home for âpackagesâ, separate from ops_templates.
So: update Juniper to keep ops_templates small and global; add full packages only for recurring jobs (and store them in runbooks or ops_job_packets), not everywhere.
OK, can you write that update prompt for me?
Juniper â Update: keep ops_templates global, add ops_job_packets for recurring âfull packagesâ
You are Juniper. Task: implement a clean separation between (A) council-wide templates and (B) recurring job packages, so we donât bloat ops_templates or duplicate long prompts across runbooks.
GOAL
- Keep ops_templates small and global (headers + generic templates only).
- Store recurring job packages (invocation + scope ticket + task packet) in a dedicated table ops_job_packets.
- Update runbooks and scheduled job rows to reference ops_job_packets instead of embedding huge text everywhere.
SAFETY
- Do not paste tokens/secrets/IDs in any prompt text.
- Additive changes only.
- Old ACT v2 / 3-gate capability protocol is deprecated; do not reference it.
PART 1 â Confirm ops_templates scope (global only)
Ensure ops_templates contains only:
- invocation_header_v1
- scope_ticket_template_v1
- task_packet_header_v1
Optional globals:
- runreport_contract_v1
- deprecation_notice_v1
- council_cheatsheet_v1
If any agent-specific job text is currently stored in ops_templates, move it out.
PART 2 â Create ops_job_packets (Ops base â Ops Inventory or new group âJob Packetsâ)
Create table: ops_job_packets
Columns:
- job_packet_name (text unique)
- agent (select: Hazel | Olive | Rosemary | Holly | Judy | Juniper | other)
- project (select: ops | reels | watch | property | other)
- cadence (select: adhoc | daily | weekly | hourly | frequent)
- version (text)
- status (select: active | deprecated)
- invocation_text (long text)
- scope_ticket_text (long text)
- task_packet_text (long text)
- last_updated_at (datetime)
- notes (long text nullable)
PART 3 â Seed recurring job packets (start minimal)
Create ACTIVE packets for:
1) Hazel Daily Ops Maintenance
- job_packet_name = hazel_daily_ops_maintenance_v1
- cadence = daily
- invocation/scope/task = use the canonical texts we already drafted
2) Hazel Weekly Ops Maintenance
- job_packet_name = hazel_weekly_ops_maintenance_v1
- cadence = weekly
- invocation/scope/task = use the canonical texts we already drafted
Optional placeholders (create later, donât block this task):
- olive_reels_segment_batch_v1 (reels)
- rosemary_watch_nightly_enrichment_v1 (watch)
- holly_property_index_batch_v1 (property)
PART 4 â Wire scheduled jobs to packets (ops_scheduled_jobs)
For hazel_daily_ops_maintenance and hazel_weekly_ops_maintenance rows:
- Set script_or_command_ref (or notes) to:
âUse ops_job_packets.<job_packet_name>â
- Keep last_run_report link usage as-is
PART 5 â Update runbooks to reference packets (reduce redundancy)
In Hazel runbook:
- Replace embedded daily/weekly full text with:
âCanonical job packet: ops_job_packets.hazel_daily_ops_maintenance_v1â
âCanonical job packet: ops_job_packets.hazel_weekly_ops_maintenance_v1â
- Keep only Hazel-specific policies and links to views in the runbook.
In Juniper runbook:
- Add a short rule:
âRecurring jobs live in ops_job_packets; global boilerplate lives in ops_templates.â
DELIVERABLE
- ops_job_packets table exists
- Hazel daily/weekly packets seeded and marked active
- ops_scheduled_jobs rows point to the packets
- Hazel runbook deduped to reference packets
- Post ops_run_reports (project=ops) summarizing what changed + where the canonical packets live
OK, now can you give me the set of short invocations for all agent jobs that we have set up now for me to give to juniper?
Hereâs a clean set of short invocation bodies (no boilerplate) for the agent jobs weâve set up so far. Tell Juniper to prepend ops_templates.invocation_header_v1 to each before storing/scheduling.
Iâm writing them as âInvocation Bodiesâ so they stay copy/paste short.
Hazel (Ops)
1) Hazel â Daily Ops Maintenance
Hazel: Run job packet `hazel_daily_ops_maintenance_v1` (or âDaily Ops Maintenanceâ per your runbook).
- Create ops_run_reports (project=ops) + heartbeat.
- Use canonical views: Services Down/Unknown, Jobs Stale/Failing, Websites Persistent/Deprecated/Port Conflicts, Hosts Offline.
- Safe remediation only (bounded restarts; no reboot by default).
- Close ops_run_reports + update ops_scheduled_jobs health fields for hazel_daily_ops_maintenance.
2) Hazel â Weekly Ops Maintenance
Hazel: Run job packet `hazel_weekly_ops_maintenance_v1` (or âWeekly Ops Maintenanceâ per your runbook).
- Create ops_run_reports (project=ops) + heartbeat.
- Reconcile inventory drift (hosts/services/websites/launch agents), deprecated cleanup plan, recurring failure analysis.
- Propose 1â3 runbook change requests.
- Close ops_run_reports + update ops_scheduled_jobs health fields for hazel_weekly_ops_maintenance.
Judy (Compliance Auditor)
3) Judy â Daily Compliance Audit (lightweight)
Judy: Run a daily compliance audit.
- Rehydrate: agent_runbooks (Judy + Rosemary/Olive/Holly) + ops_run_reports.
- Audit last N=2 runs per agent (Rosemary/Olive/Holly) with universal rubric.
- Write rows to Ops base â compliance_audits (link to ops_run_reports).
- Post a short summary: top failures + 1â3 fixes.
4) Judy â Weekly Deep Audit
Judy: Run a weekly deep compliance audit.
- Audit last N=5 runs per agent (Rosemary/Olive/Holly).
- Identify recurring failure patterns + draft explicit runbook change requests.
- Write to compliance_audits and summarize priorities.
Olive (Reels)
5) Olive â OpenSceneSense 5âminute Sample (approval gate)
Olive: Run OpenSceneSense-Ollama 5âminute sample on one reel_media_asset.
SCOPE:
- reel_media_asset_id = <PASTE_ID> (or immich_asset_id/source_locator)
OUTPUTS:
- sample clip (5 min)
- raw JSON output
- annotated segment list (MD/CSV/JSON)
- repeatable âhow to process a reelâ report
Log to ops_run_reports (project=reels) with links to artifacts.
6) Olive â Ongoing Reel Processing (queue-based segment batch)
Olive: Process the next reel from âReel Assets â Readyâ using OpenSceneSense segment batches.
- One reel at a time.
- Segment duration 300s; process up to 6 segments per run (or timebox).
- Write deterministic scene rows + tags (openscenesense + needs_review) + artifacts + reel report.
- Update reel_media_assets status/progress markers.
- Log to ops_run_reports (project=reels) with links.
Rosemary (Watch)
7) Rosemary â Watch Batch (import â promote evidence â cited conflicts)
Rosemary: Run one watch enrichment batch (10 watches).
- Import/normalize search JSON into search_runs/search_results (idempotent).
- Promote 1â2 curated Watch Links per watch.
- Create/open Spec Conflicts with citations (source_link_id).
- Update Watches.enrichment_status + last_enriched_at.
- Log to ops_run_reports (project=watch) with links to âOpen Conflictsâ + âLinks Created Todayâ.
Holly (Property)
8) Holly â Property Index Batch (200 docs + Review Inbox)
Holly: Run one property indexing batch.
SCOPE:
- source_scope = <PASTE_FOLDER_OR_LABEL>
- Queue/dedupe up to 200 docs in property_documents.
- Classify property_id using alias+path scoring + normalizer.
- Extract vendors/contracts/invoices/issues/schedules only when confident.
- Ambiguity/sensitivity â Review Inbox entry (needs_review + reason + next_step).
- Log to ops_run_reports (project=property) with link to Review Inbox view.
If you want, tell me what youâve actually scheduled so far (daily/weekly only, or also nightly watch + reel processing), and Iâll format this as a single âinvocations catalogâ table Juniper can paste into ops_job_packets or ops_scheduled_jobs.
Clean. Here's what the architecture looks like now:
ops_templates (mz1sxqr19q1s2kr) â 7 global rows only:
deprecation_notice_v1 ¡ runreport_contract_v1 ¡ nocodb_api_quickref_v1 ¡ council_standard_v1 ¡ task_packet_header_v1 ¡ invocation_header_v1 ¡ council_cheatsheet_v1
(rows 8â13 deprecated/migrated, text preserved in ops_job_packets)
ops_job_packets (mnqmhh4ip8qlnis) â 5 rows:
Row Name Status
1 hazel_daily_ops_maintenance_v1 active (full text)
2 hazel_weekly_ops_maintenance_v1 active (full text)
3 olive_reels_segment_batch_v1 active (placeholder)
4 rosemary_watch_enrichment_v1 active (placeholder)
5 holly_property_index_v1 active (placeholder)
Runbooks updated:
Hazel â v3.2 â embedded job text stripped, replaced with 12-line packet reference
Juniper â v2.2 â "Job Packet Architecture" rule added (templates=global, packets=recurring)
ops_scheduled_jobs â both Hazel rows now have script_or_command_ref pointing to packet names.
To trigger Hazel: read invocation_text from packet row 1 (daily) or row 2 (weekly) and send it.
Thatâs a really clean state. Youâve got the separation exactly right now:
ops_templates= global boilerplate onlyops_job_packets= recurring jobs (full text lives here)- runbooks = thin, reference packets
ops_scheduled_jobspoints to packet names
Immediate next steps Iâd do (in order):
-
Promote the 3 placeholder packets to full text
olive_reels_segment_batch_v1rosemary_watch_enrichment_v1holly_property_index_v1
(You already have the content from earlier; just paste it in and mark them âfull textâ.)
-
Add/confirm each packet has a real
invocation_text- so Juniper (or a scheduler) can trigger without editing
- Once Olive/Rosemary/Holly packets are full, add them to
ops_scheduled_jobsonly if you truly want them on a cadence (otherwise keep them manual/on-demand).
If you paste the current placeholder text for those three packets, I can rewrite each into the final âfull textâ packet format that matches how youâre storing Hazelâs packets now.
Here's what each row for HAzel Olive and Rosemary has right now â pretty thin. For reference, here's the structure Hazel's packets follow so you can match the format:
invocation_text â trigger only: invocation header boilerplate + agent name + which scope/task packet to use + post-run instructions (close ops_run_reports, update health fields)
scope_ticket_text â one scope variable, selection rule (which view/table to draw from), quota/stop conditions, inputs, writeback targets, success criteria
task_packet_text â task packet header system rules block + numbered sections: 1) Goal, 2) Scope, 3) Quota/Stop, 4) Writeback, 5) Safety/Policy rules, 6) Closeout requirements
Trigger-ready (no editing needed):
â
hazel_daily_ops_maintenance_v1 (row 1)
â
hazel_weekly_ops_maintenance_v1 (row 2)
Need full text before they can be triggered:
â ď¸ olive_reels_segment_batch_v1 (row 3)
â ď¸ rosemary_watch_enrichment_v1 (row 4)
â ď¸ holly_property_index_v1 (row 5)
Whenever you paste the full text for rows 3â5, I'll write them straight in.
Can you rewrite the invocation and complete scope ticket and task packet for Olive RoseMAry and Holly?
Below are full, Hazel-format job packets for rows 3â5. Each one contains:
invocation_text(trigger-only, includes invocation header boilerplate)scope_ticket_text(one scope variable + selection rule + quota/stop + inputs/writeback/success)task_packet_text(task packet header system rules + numbered sections)
You can paste each block into the corresponding ops_job_packets row fields.
Row 3 â olive_reels_segment_batch_v1
invocation_text
[INVOCATION HEADER â READ FIRST]
This message is an INVOCATION (a trigger), not a full spec.
Do NOT improvise policy or invent new procedures.
MANDATORY BEFORE EXECUTION:
1) Rehydrate from Ops base â agent_runbooks â your agent row (canonical spec).
2) Create an ops_run_reports row BEFORE starting work (status=running + heartbeat).
3) Follow the referenced Task Packet / runbook procedure exactly.
4) If any required table/view/schema is missing â mark ops_run_reports=blocked with next_step and STOP.
5) Never self-kill slow work. Checkpoint + ask if suspected hang.
6) Never paste secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / capability scoring protocols are deprecated.
- Trello is backlog only; outputs/visibility go to ops_run_reports + project DB tables.
Olive: Run âReels Segment Batchâ now using:
- Scope Ticket: OLIVE_REELS_SEGMENT_SCOPE_TICKET_v1
- Task Packet: OLIVE_REELS_SEGMENT_TASK_PACKET_v1
Post-run requirements:
- Close ops_run_reports with counts + links (Ready/Processing/Needs Review views + reel report artifact)
- Update reel_media_assets.status/progress markers for the reel processed this run
scope_ticket_text
SCOPE TICKET (v1)
Title: Olive â Reels Segment Batch (queue-based)
Project: reels
Agent: Olive
Related Task Packet: OLIVE_REELS_SEGMENT_TASK_PACKET_v1
Single Scope Variable:
- queue_view = âReel Assets â Readyâ (Reel Archive base â reel_media_assets)
Selection rule:
- Pick the TOP 1 reel_media_asset from âReel Assets â Readyâ
- Only eligible if (source_locator not empty OR immich_asset_id not empty)
Quota / Stop:
- Segment duration: 300s (5 minutes)
- Per run: process up to 6 segments (â30 min footage) OR stop at 3 hours
- Stop early if: ffmpeg missing, Ollama unreachable, OpenSceneSense not runnable, DB writes failing
Inputs:
- Reel Archive base:
- reel_media_assets view âReel Assets â Readyâ
- scenes + scene_tags (and artifacts/jobs tables if present)
- OpenSceneSense-Ollama installed & working (local)
- Source video via reel_media_assets.source_locator or immich_asset_id
Writeback targets:
- reel_media_assets: status/progress fields (processing/needs_review/error; next_segment_start_s; last_processed_at; last_error)
- scenes: deterministic scene_key per segment, start/end timecodes, status=needs_review
- scene_tags: openscenesense + needs_review per scene
- artifacts: store JSON outputs + per-reel report
- ops_run_reports: run log + links
Success criteria:
- One reel advanced by up to 6 segments with no duplicate scenes
- Outputs visible in DB + artifacts linked
- ops_run_reports closed done/blocked with links + next steps
task_packet_text
[TASK PACKET HEADER â SYSTEM RULES]
SOURCE OF TRUTH:
- Runbook (Ops base â agent_runbooks â your row) is the canonical spec.
- ops_run_reports is the canonical execution log.
- Project DB tables are the canonical artifacts (not hidden files).
- Trello is planning/backlog only.
MANDATORY EXECUTION CONTRACT:
- Create ops_run_reports row before work; heartbeat every 30â60 min (+ per-unit thresholds if defined).
- Work in bounded batches (quota + time limit). Stop when quota/time limit hit.
- If uncertain: route to needs_review / Review Inbox; do not guess.
- Never self-kill slow work; checkpoint + ask if suspected hang.
- Never include secrets/tokens/credentials in summaries or artifacts.
DEPRECATION:
- Old ACT v2 / 3-gate capability protocol is deprecated.
DONE CRITERIA (always):
- ops_run_reports closed done/blocked with counts + links to outputs/views.
- Canonical tables updated as specified.
TASK PACKET (v1)
Title: OLIVE_REELS_SEGMENT_TASK_PACKET_v1 â Reels Segment Batch (OpenSceneSense)
Project: reels
Assigned agent: Olive
Runbook: Ops base â agent_runbooks â Olive
1) Goal
Process the next reel from the Ready queue in bounded 5âminute segments using OpenSceneSense-Ollama and write reviewable âscene segmentsâ into the Reels DB.
2) Scope
- One reel_media_asset per run, selected from âReel Assets â Readyâ
- Treat outputs as segments (reviewable units), not perfect cut detection
- No cross-reel processing
3) Quota / Stop conditions
- segment_duration_s = 300
- segments_per_run = 6 OR 3 hours max
- Stop early if missing prerequisites or DB write errors
4) Writeback targets (must happen)
- reel_media_assets:
- status: newâprocessingâneeds_review/processed/error
- next_segment_start_s increments
- last_processed_at updated
- last_error on failure
- scenes:
- one scene row per segment (v1)
- scene_key deterministic: "<reel_media_asset_id>@<start>-<end>"
- start_timecode/end_timecode set
- status=needs_review
- scene_tags:
- ensure tags âopenscenesenseâ and âneeds_reviewâ exist and are attached
- artifacts:
- save raw JSON output per segment
- update perâreel report artifact (md/html)
- ops_run_reports:
- progress_unit=segments; progress_total=6; heartbeat every segment + 30â60 min
5) Safety / Policy rules
- Never self-kill slow work; checkpoint + ask if suspected hang
- Do not invent people/events/locations unless explicit/high confidence (default: needs_review)
- Do not proceed if source video is missing
6) Closeout requirements
- ops_run_reports must include:
- reel_media_asset_id processed
- segments processed count
- scenes upserted count
- link to reel report artifact
- links to âReel Assets â Processing/Needs Reviewâ views and any âScenes â Needs Reviewâ view
- If blocked: status=blocked + blocker + exact next step
Row 4 â rosemary_watch_enrichment_v1
invocation_text
[INVOCATION HEADER â READ FIRST]
This message is an INVOCATION (a trigger), not a full spec.
Do NOT improvise policy or invent new procedures.
MANDATORY BEFORE EXECUTION:
1) Rehydrate from Ops base â agent_runbooks â your agent row (canonical spec).
2) Create an ops_run_reports row BEFORE starting work (status=running + heartbeat).
3) Follow the referenced Task Packet / runbook procedure exactly.
4) If any required table/view/schema is missing â mark ops_run_reports=blocked with next_step and STOP.
5) Never self-kill slow work. Checkpoint + ask if suspected hang.
6) Never paste secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / capability scoring protocols are deprecated.
- Trello is backlog only; outputs/visibility go to ops_run_reports + project DB tables.
Rosemary: Run âWatch Enrichment Batchâ now using:
- Scope Ticket: ROSEMARY_WATCH_SCOPE_TICKET_v1
- Task Packet: ROSEMARY_WATCH_TASK_PACKET_v1
Post-run requirements:
- Close ops_run_reports with links to Watch views (Search Runs Today, Links Created Today, Open Conflicts, Missing Citation)
- Update Watches.enrichment_status + last_enriched_at for touched watches
scope_ticket_text
SCOPE TICKET (v1)
Title: Rosemary â Watch Enrichment Batch (evidence-first)
Project: watch
Agent: Rosemary
Related Task Packet: ROSEMARY_WATCH_TASK_PACKET_v1
Single Scope Variable:
- watch_batch = â10 watches from Watches â Missing Sources (fallback: Watches â Has Conflicts)â
Selection rule:
- Prefer Watches where links_count=0 (Missing Sources)
- If fewer than 10, fill remainder from watches with open conflicts
- Max 10 watches per run; max 2 fields per watch per run
Quota / Stop:
- Time limit: 90 minutes
- Stop early if: search_runs/search_results schema missing, Watch Links missing, Spec Conflicts missing citation link field
Inputs:
- JSON inputs (if used):
- /tmp/watch_research_results.json
- /projects/watch-vault/data/research_queue.json
- Watch DB tables:
Watches, Watch Links, Watch Specs, Spec Conflicts, search_runs, search_results
Writeback targets:
- search_runs/search_results (idempotent import)
- Watch Links (curated evidence)
- Spec Conflicts (open conflicts with citations: source_link_id)
- Watches (enrichment_status + last_enriched_at)
- ops_run_reports
Success criteria:
- 10 watches touched OR clean stop
- New Watch Links created with citations/backrefs
- New/open Spec Conflicts created with source_link_id
- ops_run_reports closed with counts + links to views
task_packet_text
[TASK PACKET HEADER â SYSTEM RULES]
SOURCE OF TRUTH:
- Runbook (Ops base â agent_runbooks â your row) is the canonical spec.
- ops_run_reports is the canonical execution log.
- Project DB tables are the canonical artifacts (not hidden files).
- Trello is planning/backlog only.
MANDATORY EXECUTION CONTRACT:
- Create ops_run_reports row before work; heartbeat every 30â60 min (+ per-unit thresholds if defined).
- Work in bounded batches (quota + time limit). Stop when quota/time limit hit.
- If uncertain: route to needs_review / Review Inbox; do not guess.
- Never self-kill slow work; checkpoint + ask if suspected hang.
- Never include secrets/tokens/credentials in summaries or artifacts.
DEPRECATION:
- Old ACT v2 / 3-gate capability protocol is deprecated.
DONE CRITERIA (always):
- ops_run_reports closed done/blocked with counts + links to outputs/views.
- Canonical tables updated as specified.
TASK PACKET (v1)
Title: ROSEMARY_WATCH_TASK_PACKET_v1 â Watch Enrichment Batch (Evidence-first)
Project: watch
Assigned agent: Rosemary
Runbook: Ops base â agent_runbooks â Rosemary
1) Goal
Normalize search inputs (if present), promote curated evidence (Watch Links), and open Spec Conflicts with citations for a bounded watch batch.
2) Scope
- Max 10 watches per run
- Max 2 fields per watch per run
- Evidence-first: do not write into Watch Specs unless explicitly allowed by runbook and strongly supported
3) Quota / Stop conditions
- 90 minutes max
- Stop early and block if citation linkage isnât available (source_link_id missing)
4) Writeback targets
- search_runs/search_results:
- idempotent import (fingerprint/normalized_status)
- Watch Links:
- create 1â2 curated links per watch (max)
- store url/title/snippet excerpt, retrieved_at, archived_copy_uri
- backrefs to search_run/search_result where available
- Spec Conflicts:
- create/open conflicts only when New Value is clearly stated
- always set source_link_id (preferred) or source_url (fallback)
- resolution=open by default; do not guess
- Watches:
- update last_enriched_at
- enrichment_status:
- needs_review if any open conflicts created
- in_progress if only evidence added
5) Safety / Policy rules
- No guessing from vague snippets
- Do not overwrite collector narrative fields
- Never paste secrets/tokens
6) Closeout requirements
- ops_run_reports must include:
- runs imported / results inserted counts (if applicable)
- watch_links created count
- spec_conflicts created count
- watches touched count
- links to: Search Runs Today, Links Created Today, Open Conflicts, Open Missing Citation
- If blocked: status=blocked + blocker + exact schema/next step
Row 5 â holly_property_index_v1
invocation_text
[INVOCATION HEADER â READ FIRST]
This message is an INVOCATION (a trigger), not a full spec.
Do NOT improvise policy or invent new procedures.
MANDATORY BEFORE EXECUTION:
1) Rehydrate from Ops base â agent_runbooks â your agent row (canonical spec).
2) Create an ops_run_reports row BEFORE starting work (status=running + heartbeat).
3) Follow the referenced Task Packet / runbook procedure exactly.
4) If any required table/view/schema is missing â mark ops_run_reports=blocked with next_step and STOP.
5) Never self-kill slow work. Checkpoint + ask if suspected hang.
6) Never paste secrets/tokens/credentials in summaries.
DEPRECATION:
- Old ACT v2 / capability scoring protocols are deprecated.
- Trello is backlog only; outputs/visibility go to ops_run_reports + project DB tables.
Holly: Run âProperty Index Batchâ now using:
- Scope Ticket: HOLLY_PROPERTY_SCOPE_TICKET_v1
- Task Packet: HOLLY_PROPERTY_TASK_PACKET_v1
Post-run requirements:
- Close ops_run_reports with link to Review Inbox view
- Update property_documents statuses and review fields
scope_ticket_text
SCOPE TICKET (v1)
Title: Holly â Property Index Batch (Review Inbox driven)
Project: property
Agent: Holly
Related Task Packet: HOLLY_PROPERTY_TASK_PACKET_v1
Single Scope Variable:
- doc_batch = âNext 200 docs from Docs â Unprocessed viewâ
Selection rule:
- Use Property base â property_documents view âDocs â Unprocessedâ
- Select next 200 docs with status in (new, needs_review) ordered by created_at asc (or id asc)
- Do NOT roam the filesystem outside what property_documents already references (bounded by the doc rows)
Quota / Stop:
- Quota: 200 docs OR 60 minutes
- Stop early if: property_documents missing, Review Inbox fields missing, DB writes failing
Inputs:
- Property base:
- property_documents + âDocs â Unprocessedâ view
- properties (nicknames/aliases)
- vendors/vendor_contracts/maintenance_log/issues/maintenance_schedule as applicable
- âReview Inboxâ view
Writeback targets:
- property_documents: classification + status + review fields + sensitivity
- vendors/vendor_contracts/maintenance_log/issues/maintenance_schedule (only when confident)
- ops_run_reports: run log + links
Success criteria:
- 200 docs processed/triaged OR clean timeboxed stop
- Ambiguity routed to Review Inbox with reason + next_step
- No secrets leaked
- ops_run_reports closed with counts + Review Inbox link
task_packet_text
[TASK PACKET HEADER â SYSTEM RULES]
SOURCE OF TRUTH:
- Runbook (Ops base â agent_runbooks â your row) is the canonical spec.
- ops_run_reports is the canonical execution log.
- Project DB tables are the canonical artifacts (not hidden files).
- Trello is planning/backlog only.
MANDATORY EXECUTION CONTRACT:
- Create ops_run_reports row before work; heartbeat every 30â60 min (+ per-unit thresholds if defined).
- Work in bounded batches (quota + time limit). Stop when quota/time limit hit.
- If uncertain: route to needs_review / Review Inbox; do not guess.
- Never self-kill slow work; checkpoint + ask if suspected hang.
- Never include secrets/tokens/credentials in summaries or artifacts.
DEPRECATION:
- Old ACT v2 / 3-gate capability protocol is deprecated.
DONE CRITERIA (always):
- ops_run_reports closed done/blocked with counts + links to outputs/views.
- Canonical tables updated as specified.
TASK PACKET (v1)
Title: HOLLY_PROPERTY_TASK_PACKET_v1 â Property Index Batch (200 docs + Review Inbox)
Project: property
Assigned agent: Holly
Runbook: Ops base â agent_runbooks â Holly
1) Goal
Process the next 200 property_documents rows into structured property tables, routing ambiguity/sensitive cases into Review Inbox with actionable next steps.
2) Scope
- Work strictly from property_documents (Docs â Unprocessed)
- Do not roam arbitrary folders beyond what the doc rows reference
- Apply alias/path scoring + normalizer from runbook for property classification
3) Quota / Stop conditions
- 200 docs OR 60 minutes
- Stop early and block if Review Inbox fields/views not available
4) Writeback targets
- property_documents:
- property_id classification (only if confident)
- doc_type, status, processed_at
- sensitivity flag for codes/account numbers
- review_status/reason/next_step for ambiguity
- vendors:
- upsert only with strong identifiers (phone/email/website)
- vendor_contracts / maintenance_log / issues / maintenance_schedule:
- create only when doc clearly supports it
- ops_run_reports:
- progress_unit=files; progress_total=200
- heartbeat every 50 docs and 30â60 min
5) Safety / Policy rules
- Never paste access codes/account numbers into summaries/review_reason/run reports
- Prefer needs_review over guessing
- Dedupe vendors/contracts when possible (name + contact)
6) Closeout requirements
- ops_run_reports must include:
- docs processed count, needs_review count, sensitive count
- vendors/contracts/issues/logs created counts
- link to Review Inbox view
- If blocked: status=blocked + blocker + next_step
If you want, I can also provide âpacket-compatibleâ view names for Watch/Property (Docs â Unprocessed, Watches â Missing Sources, etc.) so the scope tickets point to exactly one canonical view each.
provide âpacket-compatibleâ view names for Watch/Property (Docs â Unprocessed, Watches â Missing Sources, etc.) so the scope tickets point to exactly one canonical view each
Here are packet-compatible canonical view names (plus the exact filter/sort intent) for the two project bases. The goal is: each Scope Ticket can point to one view as its work queue, and the packet can link to a small fixed set of âresults/reviewâ views.
Watch Database (Rosemary) â Canonical Views
Queue (what to work next)
Watches â Missing Sources (Queue)
- Purpose: pick watches that need evidence first
- Filter:
links_count = 0(or âno Watch Links rowsâ) - Sort:
watch_code asc(orid asc)
Watches â Needs Enrichment (Queue)(optional but useful)
- Filter:
enrichment_status in (new, in_progress) - Sort:
last_enriched_at asc (nulls first)
Watches â Has Open Conflicts (Queue)
- Filter: there exists Spec Conflicts where
Resolution = open - Sort:
open_conflicts_count desc, thenlast_enriched_at asc
Run monitoring (what happened today)
Search Runs â Today
- Filter:
executed_at is today - Sort:
executed_at desc
Watch Links â Created Today
- Filter:
retrieved_at is today(or created_at today) - Sort:
retrieved_at desc
Review (human inbox)
Spec Conflicts â Open (Review Inbox)
- Filter:
Resolution = open - Sort:
Detected At desc
Spec Conflicts â Open Missing Citation
- Filter:
Resolution = open AND source_link_id is empty - Sort:
Detected At desc
Spec Conflicts â Needs Review
- Filter:
Resolution = needs_review - Sort:
Detected At desc
These 6â8 views are enough to make Rosemaryâs packet fully deterministic.
Property Handbook (Holly) â Canonical Views
Queue (what to work next)
Docs â Unprocessed (Queue)
- Table:
property_documents
- Filter:
status in (new, needs_review)(or just new, if you prefer) - Sort:
created_at asc(orid asc)
(If you want to keep âscopeâ tighter later, you can add a second queue view like âDocs â Unprocessed (By Scope Prefix)â, but start simple.)
Review inbox (human triage)
Review Inbox
- Filter:
review_status = needs_review(fallback: status=needs_review) - Sort:
review_created_at desc(fallback: created_at desc) - Columns to show: property_id, doc_type, sensitivity, summary, review_reason, review_next_step, source_locator
Ops outputs (optional but very helpful)
Issues â Open
- Filter:
status = open - Sort:
priority desc, thenreported_date desc
Maintenance â Due Soon
- Filter:
is_active = true AND next_due within next 30 days(or next_due not null, sort ascending) - Sort:
next_due asc
If you implement just the first two, Hollyâs packet works; the last two make the system feel âaliveâ.
If you want, I can also give you the exact wording to paste into each Scope Ticket so it references these views verbatim (e.g., âSelection rule: pick the first 10 rows from âWatches â Missing Sources (Queue)ââ).
yes, can you write prompt to define the canonical views and directions for Juniper to paste the exact workgin into each scope ticket to make this set of shortcuts truly usable? can you also create a table of these short prompts for quick reference (by me or my Juniper or the agents I guess)
Hereâs a copy/paste prompt for Juniper that (1) canonizes the canonical views, (2) installs the exact selection wording into each Scope Ticket / job packet, and (3) creates a quick-reference table in Ops base so you (and agents) can always find the right shortcut.
âââ
Prompt for Juniper: canonical views + scope-ticket wording + quick reference table
You are Juniper. Task: make canonical âqueue viewsâ and exact Scope Ticket selection wording truly usable by standardizing view names and creating a durable quick-reference table.
SAFETY
- No tokens/IDs/secrets in outputs.
- Additive changes only.
- Old ACT v2 protocol is deprecated; do not reference it.
PART 1 â Define canonical views (exact names) in each project base
A) Watch Database â create/confirm these saved views with EXACT names:
Queue views:
1) âWatches â Missing Sources (Queue)â
Filter: links_count = 0
Sort: watch_code asc (or id asc)
2) âWatches â Has Open Conflicts (Queue)â
Filter: Resolution=open exists (or open_conflicts_count > 0)
Sort: open_conflicts_count desc, last_enriched_at asc
Monitoring views:
3) âSearch Runs â Todayâ
Filter: executed_at is today
Sort: executed_at desc
4) âWatch Links â Created Todayâ
Filter: retrieved_at/created_at is today
Sort: retrieved_at desc
Review views:
5) âSpec Conflicts â Open (Review Inbox)â
Filter: Resolution = open
Sort: Detected At desc
6) âSpec Conflicts â Open Missing Citationâ
Filter: Resolution=open AND source_link_id is empty
Sort: Detected At desc
B) Property Handbook â create/confirm these saved views with EXACT names:
Queue:
1) âDocs â Unprocessed (Queue)â
Filter: status in (new, needs_review)
Sort: created_at asc (or id asc)
Review:
2) âReview Inboxâ
Filter: review_status = needs_review (fallback: status=needs_review)
Sort: review_created_at desc (fallback: created_at desc)
(Do not add lots more views now; these are the canonical ones packets will reference.)
PART 2 â Install exact selection wording into Scope Tickets / job packets
Update ops_job_packets rows (or scope_ticket_text fields) so Selection rule uses this exact wording:
A) Rosemary batch selection (10 watches):
Selection rule (paste verbatim):
- Open Watch DB view âWatches â Missing Sources (Queue)â.
- Select the first 10 watches (in the viewâs sort order).
- If fewer than 10 exist, open âWatches â Has Open Conflicts (Queue)â and select the remaining watches to reach 10.
- Max 2 fields per watch this run.
B) Holly batch selection (200 docs):
Selection rule (paste verbatim):
- Open Property DB view âDocs â Unprocessed (Queue)â.
- Select the first 200 docs (in the viewâs sort order).
- Process ONLY those 200 docs this run (do not roam beyond the queued doc rows).
(If you also want Oliveâs queue wording consistent:)
C) Olive reel selection (1 reel):
Selection rule (paste verbatim):
- Open Reel Archive view âReel Assets â Readyâ.
- Select the top 1 reel_media_asset (in the viewâs sort order).
- Process ONLY that reel this run.
PART 3 â Create quick reference table in Ops base (âshortcutsâ)
In Ops base, create table: ops_shortcuts (or ops_reference_prompts)
Columns:
- shortcut_name (text unique)
- project (select: ops | reels | watch | property)
- used_by_agent (text)
- type (select: view | selection_rule | invocation)
- base_and_location (text) // e.g., âWatch Database â Watches tableâ
- exact_text (long text) // the exact snippet to copy/paste
- notes (text nullable)
Seed rows for at least:
- Watches â Missing Sources (Queue) [view]
- Watches â Has Open Conflicts (Queue) [view]
- Rosemary selection rule (10 watches) [selection_rule]
- Docs â Unprocessed (Queue) [view]
- Review Inbox [view]
- Holly selection rule (200 docs) [selection_rule]
(Optionally also store the âinvocation bodiesâ for each recurring job here, but packets already store them.)
PART 4 â Wire shortcuts into runbooks
Update runbooks (Juniper + Rosemary + Holly) with a single line:
- âCanonical shortcuts live in Ops base â ops_shortcuts. Do not invent new view names.â
DELIVERABLE
- Confirm canonical views exist (exact names).
- ops_shortcuts table exists and is populated.
- Scope Ticket selection rules in job packets reference the canonical views verbatim.
- Post ops_run_reports (project=ops) summarizing what was standardized and where shortcuts live.
âââ
Quick-reference table (preview you can paste into ops_shortcuts)
If you want to seed it manually, here are the core rows:
| shortcut_name | project | used_by_agent | type | exact_text |
| ------------------------------------ | -------- | ------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Watches â Missing Sources (Queue) | watch | Rosemary | view | Open Watch DB view âWatches â Missing Sources (Queue)â. |
| Watches â Has Open Conflicts (Queue) | watch | Rosemary | view | Open Watch DB view âWatches â Has Open Conflicts (Queue)â. |
| Rosemary â Select 10 watches | watch | Rosemary | selection_rule | Open âWatches â Missing Sources (Queue)â. Select first 10. If fewer, fill remainder from âWatches â Has Open Conflicts (Queue)â. Max 2 fields/watch. |
| Docs â Unprocessed (Queue) | property | Holly | view | Open Property DB view âDocs â Unprocessed (Queue)â. |
| Review Inbox | property | Holly | view | Open Property DB view âReview Inboxâ. |
| Holly â Select 200 docs | property | Holly | selection_rule | Open âDocs â Unprocessed (Queue)â. Select first 200 docs. Process only those 200 docs.
If you want, I can also add Oliveâs queue shortcuts (Ready/Processing/Needs Review/Error) in the same table so Hazel/Judy can reference them too.
yes add olive's shortcuts.
Add these rows to ops_shortcuts (or whatever you named the table). Iâm including both the view shortcuts and the selection rule shortcut Oliveâs Scope Ticket uses.
Olive / Reels â canonical shortcuts
| shortcut_name | project | used_by_agent | type | base_and_location | exact_text |
| ------------------------------ | ------- | ------------- | -------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Reel Assets â Ready | reels | Olive | view | Reel Archive base â reel_media_assets | Open Reel Archive view âReel Assets â Readyâ. |
| Reel Assets â Processing | reels | Olive | view | Reel Archive base â reel_media_assets | Open Reel Archive view âReel Assets â Processingâ. |
| Reel Assets â Needs Review | reels | Olive | view | Reel Archive base â reel_media_assets | Open Reel Archive view âReel Assets â Needs Reviewâ. |
| Reel Assets â Error | reels | Olive | view | Reel Archive base â reel_media_assets | Open Reel Archive view âReel Assets â Errorâ. |
| Scenes â Needs Review (Recent) | reels | Olive | view | Reel Archive base â scenes | Open Reel Archive view âScenes â Needs Review (Recent)â (or filter scenes where status=needs_review, sorted newest first). |
| Olive â Select 1 reel | reels | Olive
If you want one more (useful for Hazel/Judy too):
| shortcut_name | project | used_by_agent | type | base_and_location | exact_text |
| -------------------------- | ------- | ------------- | -------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reels â Queue Health Check | reels | Hazel/Judy | selection_rule | Ops base + Reel Archive base | Check counts in: âReel Assets â Readyâ, âProcessingâ, âNeeds Reviewâ, âErrorâ. If Processing is stale (last_processed_at old), flag in ops_run_reports/compliance_audits. |
If you want, paste your actual view names for Scenes â Needs Review (Recent) (if it differs) and Iâll align the exact_text so it matches your DB verbatim.
that is the name
Perfect â then keep the shortcut exactly as:
- View name:
Scenes â Needs Review (Recent)
and use that verbatim in:
ops_shortcuts.exact_text- Oliveâs runbook âCanonical Queue Viewsâ
- Oliveâs Scope Ticket / Task Packet links section (as the review destination after each run).
I know that I shoudl not be afraid of this, but I am afraid that when I reset juniper's context she will forget how to do any of this. and the same with the other agents. What prompt do I give to an agent that is freshly booted up with no context, and seems to not know anything.
Youâre not being irrational â agents do forget. The whole reason you built agent_runbooks + ops_templates + ops_job_packets is so a âblankâ agent can rehydrate itself in 60â120 seconds.
What you want is a single Fresh Boot / Amnesia Recovery prompt that forces the agent to:
- open the Ops base
- read its runbook + the council cheat sheet
- find the active job packets
- only then act
Copy/paste this into any freshly-booted agent (Juniper, Hazel, Olive, Rosemary, Holly, Judy). Just fill the <AGENT_NAME>.
Fresh Boot / No Context Recovery Prompt (universal)
You are <AGENT_NAME>. You have no context. Do NOT guess.
Your job is to rehydrate from the durable system (Ops base) and then either (a) run the next job packet or (b) report whatâs missing.
STEP 1 â Rehydrate from Ops base (mandatory)
Open the NocoDB Ops base and read, in order:
1) ops_templates:
- council_cheatsheet_v1
- deprecation_notice_v1
- runreport_contract_v1
- council_standard_v1
2) agent_runbooks:
- your row (agent=<AGENT_NAME>)
3) ops_job_packets:
- list all rows where status=active
4) ops_shortcuts:
- find the shortcuts relevant to your agent/project (queue views + selection rules)
5) ops_run_reports:
- check for stale/blocked runs related to your agent/project
STEP 2 â Summarize what you learned (short, structured)
Reply with:
- Your role (1 sentence)
- Which job packets are active for you (names)
- Which queue view you will use (exact name)
- Any blockers you detected (missing views/tables/fields)
STEP 3 â If instructed to run work
Only run a job if:
- you have a matching active job packet for it, AND
- you can create an ops_run_reports row, AND
- you know the canonical queue view to draw from.
When running:
- follow runreport_contract_v1 exactly
- follow your agent_runbook as the spec
- treat Trello as backlog only
- never paste tokens/secrets/credentials
STEP 4 â If anything is missing
Do NOT improvise.
Mark blocked (in ops_run_reports if possible) with:
- blocker: what is missing
- next_step: the exact human action needed
Then stop.
If the agent is Juniper specifically
Add one extra line at the end of Step 1:
- âAlso check that ops_templates has only global templates and ops_job_packets holds recurring job text; scan agent_runbooks for redundant boilerplate and replace with references.â
If you want, I can give you a one-liner version you can send in a panic (âJuniper, rehydrate from ops_templates/agent_runbooks/ops_job_packets and tell me what job you can run nextâ), but the longer version above is what reliably prevents hallucinated âI think the system isâŚâ behavior.
, yes give me one liners also, I will use them sparingly
Here are one-liners you can drop into chat when an agent is âblankâ or drifting. Theyâre intentionally strict.
Universal (any agent)
-
Rehydrate-only:
Rehydrate from Ops base (ops_templates â council_cheatsheet + deprecation + runreport_contract, then your agent_runbooks row, then list active ops_job_packets for you). Reply with next action + blockers. Do not run anything yet. -
Rehydrate + run next packet:
Rehydrate from Ops base, then run the next appropriate active ops_job_packets entry for your role. Log to ops_run_reports with heartbeats and closeout links.
Juniper
Juniper: Rehydrate from Ops base, verify ops_templates are global-only and ops_job_packets hold recurring jobs, scan runbooks for redundant boilerplate, then tell me the next 3 actions and any blockers.
Hazel
Hazel: Rehydrate, then run ops_job_packets.hazel_daily_ops_maintenance_v1 exactly; log to ops_run_reports + update ops_scheduled_jobs health fields.
Olive
Olive: Rehydrate, then run ops_job_packets.olive_reels_segment_batch_v1 (queue: âReel Assets â Readyâ); log to ops_run_reports with reel report links.
Rosemary
Rosemary: Rehydrate, then run ops_job_packets.rosemary_watch_enrichment_v1 (10 watches from âWatches â Missing Sources (Queue)â); log to ops_run_reports with conflicts/links views.
Holly
Holly: Rehydrate, then run ops_job_packets.holly_property_index_v1 (200 docs from âDocs â Unprocessed (Queue)â); route ambiguity to Review Inbox; log to ops_run_reports.
Judy
Judy: Rehydrate, audit last 2 runs for Rosemary/Olive/Holly using your rubric, write rows to compliance_audits, summarize top issues.
I created these views but
View Table Blocker
Watches â Missing Sources (Queue) Watches Needs links_count rollup column first
Watches â Has Open Conflicts (Queue) Watches Needs open_conflicts_count rollup first
Spec Conflicts â Open Missing Citation Spec Conflicts filter: resolution=open AND source_link_id empty
Resolution does not have default values (open? is that the same as null) and there is no source_link_id and is empty = null?
Youâre running into two very normal âfirst time NocoDB schemaâ friction points:
- queues that depend on rollups (
links_count,open_conflicts_count) - âopenâ state + missing citation when fields/defaults donât exist yet
Hereâs the clean way to get unstuck.
A) Watches â Missing Sources (Queue) needs a real relationship + count
You have two ways to implement it:
Option A1 (recommended): create links_count rollup on Watches
Prereq: Watch Links must link to Watches.
- Ensure
Watch Links.watchis a Link to Watches (not text) - Then in Watches, add a Rollup field:
- Name:
links_count
- Name:
- Relation: the linked Watch Links relationship
- Function:
COUNT
- Function:
Then the view works:
- Filter:
links_count = 0 - Sort:
watch_code asc
Option A2 (works if rollups are annoying): filter on âlinked records is emptyâ
If Watches has a visible âlinked Watch Linksâ relation field, you can make the view:
- Filter:
Watch Linksis empty
(no rollup needed)
B) Watches â Has Open Conflicts (Queue) â easiest is to NOT do this as a Watches view (yet)
Filtering watches by âhas open conflictsâ is harder because it requires either:
- an
open_conflicts_countrollup (which needs a way to count only Resolution=open), or
- a formula + rollup-sum
Fastest path: donât block on this. Use the Spec Conflicts table as the queue.
Instead, create:
Spec Conflicts â Open (Review Inbox)- Filter:
Resolution is empty OR Resolution = open - Sort:
Detected At desc
- Filter:
Rosemary can still do her work:
- pull watches from Missing Sources
- handle conflicts from Spec Conflicts â Open
If you really want the Watches-level queue later, do it after the pipeline is stable.
C) Spec Conflicts â Open Missing Citation needs two fixes
1) Treat NULL as âopenâ
Yes: if Resolution has no default values yet, treat empty/null as âopen/unresolved.â
So your âopenâ filter should be:
Resolution is empty OR Resolution = open
(Once you set a default later, most new rows will be explicit open, but the view stays backward compatible.)
2) You need source_link_id
Right now you canât filter âmissing citationâ because the column doesnât exist.
Do this (non-destructive):
- Add
source_link_idto Spec Conflicts as a Link to Watch Links (nullable) - Keep legacy
Sourcetext as-is (it points to JSON dumps)
Then your view becomes:
Spec Conflicts â Open Missing Citation
- Filter:
- (
Resolution is empty OR Resolution = open) - AND (
source_link_id is empty)
- (
- Sort:
Detected At desc
Thatâs the exact behavior you want.
Minimal checklist (in the order Iâd do it)
- Ensure
Watch Links.watchis a Link to Watches - Add
Watches.links_countrollup (COUNT of related Watch Links) - Add
Spec Conflicts.source_link_id(Link â Watch Links) - Create views:
- Watches â Missing Sources (Queue): links_count=0
- Spec Conflicts â Open (Review Inbox): Resolution empty OR open
- Spec Conflicts â Open Missing Citation: open AND source_link_id empty
If you paste the field types you currently have for:
Watch Links.watch(link or text?)Spec Conflicts.Watch/Spec Conflicts.Watch ID
I can tell you the exact âconvert vs add new link fieldâ move that avoids breaking existing data.
both Watch Links.watch and Spec COnflicts.watcj is text Watch ID, Right now these tables are empty and so can be changed if we update the schema and Rosemary
Spec Conflicts.source_link_id is also text
Perfect â if both tables are empty, youâre in the best possible situation: you can fix the schema cleanly with no migration pain.
Hereâs the cleanest path:
1) Convert those text âWatch IDâ fields into real links (do this now)
Watch Links
- Change
Watch Links.watchfrom text â Link to Watches - (Optional) keep a separate text field
watch_codeif you want the PT-097 visible without clicking, but itâs not required.
Spec Conflicts
- Change
Spec Conflicts.watchfrom text â Link to Watches - Keep
Spec Conflicts."Watch ID"as legacy display text only if you want it; otherwise you can drop it since the link is the real FK.
Because the tables are empty, you can safely change field types rather than adding parallel columns.
2) Add the missing citation + status fields
In Spec Conflicts, add:
source_link_id(Link â Watch Links)- Make
Resolutiona Single Select with values:open,accepted,rejected,needs_review
- Set default =
openfor Resolution (so âopenâ isnât ambiguous/null going forward)
Now your âOpen Missing Citationâ view works exactly as intended.
3) Add rollups on Watches
Once the links exist, in Watches add:
links_count= Rollup COUNT of related Watch Linksconflicts_count= Rollup COUNT of related Spec Conflicts
(You can postpone open_conflicts_count until later; itâs not necessary to get the pipeline working.)
4) Then create the canonical views
- Watches â Missing Sources (Queue):
links_count = 0 - Spec Conflicts â Open (Review Inbox):
Resolution = open
- Spec Conflicts â Open Missing Citation:
Resolution=open AND source_link_id is empty - Watch Links â Created Today:
created_at today
5) Update Rosemaryâs behavior
Once these fields exist, Rosemary should always write:
- Watch Links.watch (link to the watch record)
- Spec Conflicts.watch (link)
- Spec Conflicts.source_link_id (link)
- Resolution defaults to open, so she doesnât have to set it unless she wants.
If you want, I can write a single âJuniper schema updateâ prompt that lists the exact field edits to make in NocoDB (since empty tables make it straightforward).
Great â same story: because the table is empty, you can convert it cleanly.
What to do
- Change
Spec Conflicts.source_link_idfrom text â Link to Watch Links.
Then your âMissing Citationâ filter becomes trivial:
Resolution = open AND source_link_id is empty
Recommended âbundleâ schema fix (do all at once)
Since youâre touching the schema anyway, do this in one pass:
Watch Links.watchâ Link to WatchesSpec Conflicts.watchâ Link to WatchesSpec Conflicts.source_link_idâ Link to Watch LinksSpec Conflicts.Resolutionâ Single select with defaultopen
If you want, I can write the exact Juniper prompt that lists these 4 edits and the view creation steps in the right order (so rollups work immediately).
YEs please write the exact prompt to make these changes
Juniper â Watch DB schema fix (empty tables, safe to convert types) + rollups + canonical views
You are Juniper. Task: fix the Watch Database schema so queue views work and Rosemary can write evidence-first conflicts with real citations. The affected tables are currently EMPTY, so you may safely change field types instead of doing migrations.
SAFETY
- Additive/non-destructive where possible, but since these tables are empty, type conversions are OK.
- Do not paste tokens/IDs/secrets in any output.
- Old ACT v2 protocol is deprecated; do not reference it.
PART 1 â Convert fields from TEXT to LINK (critical)
A) Table: Watch Links
- Change field `watch` (currently text Watch ID) â Link to `Watches`
- If NocoDB wonât allow in-place type conversion, then:
1) create new field `watch_ref` (Link â Watches)
2) (optional) keep legacy `watch` text for display
3) update Rosemary to write `watch_ref` going forward
B) Table: Spec Conflicts
- Change field `watch` (currently text Watch ID) â Link to `Watches`
- Same fallback approach if needed: create `watch_ref` (Link â Watches)
- Change field `source_link_id` (currently text) â Link to `Watch Links`
- Fallback if needed: create `source_link_ref` (Link â Watch Links)
PART 2 â Normalize Resolution field (make âopenâ real, not null)
In Spec Conflicts:
- Ensure `Resolution` is a Single Select with these values:
- open
- accepted
- rejected
- needs_review
- Set default value = open
Note: for any existing rows (should be none), treat empty as open.
PART 3 â Add rollups on Watches (so queue views work)
In table: Watches
- Add Rollup field: `links_count`
- relation: Watch Links (via the link field)
- function: COUNT
- Add Rollup field: `conflicts_count`
- relation: Spec Conflicts (via the link field)
- function: COUNT
(Do NOT implement open_conflicts_count yet; we can add later if needed.)
PART 4 â Create/confirm canonical views (exact names)
A) Watches table:
1) âWatches â Missing Sources (Queue)â
- Filter: links_count = 0
- Sort: watch_code asc (or id asc)
B) Spec Conflicts table:
2) âSpec Conflicts â Open (Review Inbox)â
- Filter: Resolution = open (and optionally include empty if any legacy)
- Sort: Detected At desc
3) âSpec Conflicts â Open Missing Citationâ
- Filter: Resolution = open AND source_link_id is empty
(or source_link_ref is empty if you used the fallback field name)
- Sort: Detected At desc
C) Watch Links table:
4) âWatch Links â Created Todayâ
- Filter: retrieved_at is today (or created_at today)
- Sort: retrieved_at desc
PART 5 â Update Rosemary packet + ops_shortcuts references
- Update ops_shortcuts exact text to reference these canonical view names.
- Update ops_job_packets.rosemary_watch_enrichment_v1 scope_ticket_text to say:
âSelect first 10 watches from Watches â Missing Sources (Queue).â
DELIVERABLE
- Post ops_run_reports (project=ops) confirming:
- fields converted/created (watch link, source_link_id link)
- rollups added (links_count, conflicts_count)
- views created (names listed above)
- any fallback field names used (watch_ref/source_link_ref) so packets can reference the right one.
I also noticed that Watch Specs and Watches also only have a text Watch ID. Do wwe need to modify those tables also >
Yes â at least Watch Specs should link to Watches, otherwise Rosemary canât reliably read âExisting Valueâ (canonical spec) for a watch.
What you want long-term
Watcheshas the real UID (id) + a human code (watch_code, e.g. PTâ097)Watch Specsis 1:1 with Watches via a Link field (watchâ Watches)Watch Links.watch,Spec Conflicts.watch, etc. all point to the same Watches record
What to do in practice (safe, non-destructive)
1) Watches table
- Ensure there is an auto primary key
id(UID) - Keep the current text Watch ID column (e.g.
Id1) as legacy/human-facing - Add/ensure
watch_code(unique) and populate it from the legacy text ID if you havenât already
2) Watch Specs table (this is the key fix)
- Add a new field
watch= Link to Watches - Backfill it by matching:
Watch Specs.<legacy_watch_id_text>âWatches.watch_code(orWatches.Id1if thatâs your canonical text)
- Keep the legacy text column for now; donât delete anything.
Once that link exists, Rosemary can:
- read existing spec values from Watch Specs reliably
- create conflicts with citations without ambiguity
If you tell me the exact column name in Watch Specs that currently holds PTâ097 (often also Id1), I can write the exact Juniper prompt for this part too (same style as the Watch Links / Spec Conflicts schema fix).
IT is called Watch ID. Actually, this is the best time to fix this systemically, can you update the prompt above, but create the concept of a UUID that is the "Watch" object. and lets add it to every table, and run a one time lookup process to fill in the Watch specs table with a Watch Column by looking up the matching Id1 in Watches and watch ID in watch specs. Does that make sense? so essentially fix it now, so that watch references (which is really the heart of the whole relational database) works out of the box, andn ot with text matching
Yes, that makes sense â now is the right time to make âWatchâ a first-class object and stop relying on text matching.
One important clarification though:
- In NocoDB, relationships are strongest as Link-to-record fields (because they stay consistent even if you change codes).
- A UUID is great as a stable external identifier for scripts/exports, but the DB âheartâ should still be the link field (
watchâ Watches).
So the clean systemic fix is:
- Add a
watch_uuidtoWatches(stable external ID) - Add a
watchlink-to-record field to every watch-related table - Run a one-time backfill that links records by matching legacy text (
Id1/Watch ID) - Optionally add
watch_uuidas a lookup in each table (so you can see it everywhere without duplicating)
Below is an updated âexact Juniper promptâ that implements this.
Updated Juniper prompt: make Watch a UUID-backed object + link everything + one-time backfill
Juniper â Systemic Watch Reference Fix (UUID + link fields + one-time backfill)
You are Juniper. Task: make âWatchâ a first-class relational object so all watch references work out of the box via Link-to-record, not text matching. Add a stable UUID to Watches for scripts/exports, and backfill links once.
SAFETY
- Do not delete legacy text ID fields yet.
- Do not paste tokens/IDs/secrets in outputs.
- Additive changes preferred; conversions are OK if tables are empty.
KNOWN LEGACY FIELDS
- Watches has legacy text ID: Id1 (PT-097 style)
- Watch Specs legacy text ID column is called: âWatch IDâ
- Spec Conflicts currently has âWatch IDâ legacy text and other fields
- Watch Links / Spec Conflicts / Watch Specs exist; some may be empty
PART 1 â Create stable UUID on Watches (external identifier)
In table: Watches
1) Add field: watch_uuid (UUID or text)
- If NocoDB supports UUID type/default: use UUID v4 generation.
- If not: store as text and generate UUID values via a one-time fill process.
2) Ensure watch_uuid is populated for all existing watch rows (135).
3) Ensure watch_uuid is unique.
4) Keep Id1 as legacy human code; do not remove it.
PART 2 â Add canonical Link-to-record field `watch` everywhere
For each watch-related table, add:
- watch (Link â Watches)
Minimum tables to link now:
- Watch Specs
- Spec Conflicts
- Watch Links
- Service History
- search_runs (if present)
Optional later:
- watch_transactions, watch_valuations, watch_documents, watch_images, etc.
Optional convenience:
- Add
what is the convenience>
âConvenienceâ = optional lookup/display fields so you can see/copy/use the Watchâs UUID (and code) everywhere without duplicating data.
In NocoDB terms:
- Canonical relational field:
watch(Link â Watches) - Convenience fields (read-only lookups):
watch_code(Lookup fromwatchâ Watches.Id1 or Watches.watch_code)watch_uuid(Lookup fromwatchâ Watches.watch_uuid)
Why itâs useful
- In any table (Spec Conflicts, Watch Links, Watch Specs) you can see the watch_uuid directly in the grid and copy it into logs/scripts.
- Exports become stable (UUID doesnât change even if you rename the human code).
- Your agents/scripts can say âthis record is for watch_uuid=âŚâ without guessing which âPTâ097â you meant.
Why itâs optional
You can run perfectly fine with just the watch link field. The lookups are for human/debugging + script ergonomics.
If you want, Iâll paste the complete updated Juniper prompt again (the previous one got cut off right where the âOptional convenienceâ line was).
Can you redact all mentions of addresses in this thread?
Hi - this thread has gotten very long, and answers may not be accurate. I will close it now - please create a new thread for any new topics/issues