#Agent structure and reliability

1 messages ¡ Page 1 of 1 (latest)

regal hatch
#

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.

dark boltBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

regal hatch
#

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.

pastel lionBOT
#

You’re running into the two classic failure modes of “agent councils”:

  1. Context is not a database (agents will forget unless you force a re-load of the right artifacts every run)
  2. 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.md and 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.md with 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

  1. Ticket Quality Audit (input quality)
    Sample last 5 tickets created by Juniper:
    • did they have goal, batch size, done criteria, writeback?
  2. 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

  1. Which project is the “flagship” that must move forward this week?
  2. What does “agents get blocked and don’t do anything” look like in practice (one example)? Silent? Re-planning loop? Tool errors?
  3. 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.

regal hatch
#

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.

pastel lionBOT
#

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:
    1. write a checkpoint update (“no progress observed for X mins; here’s evidence”),
    2. 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:

  1. 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)
  2. 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)

  1. Where do you want the live alerts to land so you’ll actually see them (Discord channel, a dedicated “ops” channel, somewhere else)?
  2. 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.

regal hatch
#

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?

pastel lionBOT
#

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):

  1. Freeze changes: declare “only Hazel can touch crons” while migrating.
  2. 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
  1. Pick one flagship project and run it “V2-style” for 48 hours.
  2. 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.

regal hatch
#

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

pastel lionBOT
#

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)

  1. Reels → storytelling system → family annotation → search
  2. 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)

  1. Watch collection DB + valuation + archive + link photos
#
  1. Product idea research assistant (dossier/patents/competition)

C) Documentation (finite, can ship as “v1 handbook”)

  1. Property management handbook (+ visitor instructions)

D) Web presence (bounded, can be shipped fast)

  1. Personal website
  2. Company website

E) Hardware (depends on real-world constraints; tends to eat schedules)

  1. Arduino embedded software
  2. 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)

  1. Which is the single flagship for the next 2 months:

    • Reels storytelling system, OR
    • Watch DB, OR
    • Hardware (Arduino/enclosures), OR
    • Something else
  2. 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.

regal hatch
#

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.

pastel lionBOT
#

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.md
  • watch/IDEAS.md
  • property/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 right IDEAS.md during 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

  1. For Watch + Property, where is the source material primarily?
    • files/folders, email, cloud drive, notes app, something else?
  2. Roughly how much compute do you have for background runs (can you run 2 overnight without slowing Reels)?
  3. 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.

regal hatch
#

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.

pastel lionBOT
#

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_at
  • summary (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.

regal hatch
#

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.

pastel lionBOT
#

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:

  1. 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.
  2. 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:

  1. a row in your agent_runs/Run Report table (status + heartbeat + summary)
  2. 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.

regal hatch
#

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,

pastel lionBOT
#

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:

  • id
  • source_type (filesystem/email/drive/scan/etc.)
  • source_locator (path/message-id/url)
  • filename, mime_type, size, hash
  • created_at, modified_at
  • property_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:

  1. Create document rows (new documents table) for a batch (e.g., 200 files)
  2. 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.


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 documents rows + property_id classification where possible + vendor candidates in vendors
  • 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.

regal hatch
#

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

pastel lionBOT
#

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:

  • id
  • watch_id (FK)
  • source_type (press, manufacturer site, listing, receipt, forum, auction result, etc.)
  • url (or local file pointer)
  • retrieved_at
  • excerpt / key facts (short)
  • fields_supported (e.g., “case size”, “movement”, “MSRP”, “production year”)
  • confidence
  • archived_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_at so 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:

  1. Create 2–3 watch_sources rows (manufacturer page + one reputable secondary + one market listing/auction if available)
  2. 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
  3. If conflicting sources: set needs_review=true and 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).

regal hatch
#

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?

pastel lionBOT
#

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, maybe ops.work_queue later).
  • 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)?

regal hatch
#

they are all in ths same Postgres DB with different groups.

pastel lionBOT
#

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 later ops.artifacts, ops.work_queue if 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_codes etc.)

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).

regal hatch
#

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?

pastel lionBOT
#

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.

regal hatch
#

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:

  1. Create document rows (new documents table) for a batch (e.g., 200 files)
  2. 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.

pastel lionBOT
#

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.”

regal hatch
#

and is there a verion of this field for each Base? (URLS for websearches, reel file names )

pastel lionBOT
#

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:

  1. media_assets (or reel_files)
  • source_locator = reel filename, storage path, or Immich asset id
  • FK: reel_id / tape_id
  • metadata: duration, fps, resolution, etc.
  1. 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_at
  • status (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).

regal hatch
#

Watch Database, Propterty Handbook, Reel Database

pastel lionBOT
#

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 to watch_id)
  • Property Handbook: property_documents (files/emails/contracts/invoices/manuals → linked to property_id, vendor_id, etc.)
  • Reel Database: reel_assets (or media_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).

regal hatch
#

Reels, people, events, locations, scenes, face_frames, batches (for processing), tags, scene_tags

pastel lionBOT
#

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” equivalent
  • original_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 + confidence
  • status (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/or
  • reel_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.

regal hatch
#

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?

pastel lionBOT
#

Pattern:

  • each Olive run creates a batches row
  • that run also creates/updates an ops_run_reports row
  • batches.run_report_id (or ops_run_report_id) is a great link if you add it

What to do next (minimal change)

  1. Ensure reels has a stable source_locator (Immich asset id or path) + status
  2. Ensure scenes has timecodes + status/needs_review
  3. Optionally link batches ↔ ops_run_reports so 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:

  1. Job/run tracking (so you can see what’s running, stalled, done, failed, with progress + heartbeats)
  2. 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

  • id
  • project (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 | export
  • status (single select): queued | running | blocked | done | failed | cancelled
  • priority (number)
  • created_at
  • started_at
  • last_heartbeat_at
  • finished_at
  • requested_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)

  • id
  • job_id (FK → jobs)
  • attempt (number; 1,2,3…)
  • status (queued/running/blocked/done/failed)
  • started_at, last_heartbeat_at, finished_at
  • runner (text: Olive/local pipeline/etc.)
  • log (long text) or log_url
  • metrics (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

  • id
  • job_id
  • entity_type (single select): reel | scene | face_frame
  • entity_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 | import
  • confidence (number 0–1 or 0–100, nullable)
  • created_at
  • needs_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

  • id
  • scene_id
  • author (text)
  • note (long text)
  • created_at
  • source (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_filename
  • status + 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:

  1. Users / contributors
    If family members will edit/add info, you’ll want at least a lightweight users table or an author field everywhere (notes/tags/edits).

  2. Many-to-many links for people/events/locations (if you want them first-class)
    You currently have tags + scene_tags and separate people/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)

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.

regal hatch
#

{"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"}}

pastel lionBOT
#

This is a solid starting point — you’ve got the core entities right. The main gaps (given your “agents run 24/7” plan) are:

  1. Real job tracking (queued/running/blocked/done + heartbeats + progress + retries)
  2. Provenance on many-to-many links (who/what added a person/tag/event to a scene, with confidence + review status)
  3. 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_id link in job_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.

regal hatch
#

tables not created anywhere yet” (we can redesign freely)

pastel lionBOT
#

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_assets so 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_notes with source/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_events and scene_locations join tables similar to scene_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.

regal hatch
#

multiple

pastel lionBOT
#

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_id with 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_id to 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.

regal hatch
#

yes rewrite the full final v1 schema

pastel lionBOT
#
{
  "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"
  }
}
regal hatch
#

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

pastel lionBOT
#

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”).

regal hatch
#

Watch Database — all data copied natively ✅
Table Rows
Watches 135
Watch Specs 135
Spec Conflicts 449
Service History 0 (empty)
Watch Links 0 (empty)

pastel lionBOT
#

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)
#
  • url
  • retrieved_at
  • title (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_value
  • source_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.

regal hatch
#

Watch ID
Field

  • Existing Value

New Value

Source

  • Detected At
  • Resolved At
    • Resolution
    Keep Value
pastel lionBOT
#

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_id
  • Field ✅ = field_name
  • Existing Value ✅
  • New Value ✅ = candidate_value
  • Detected 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:

#
  1. Convert Source to a Link field to Watch Links
  • Best: change the Source column 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 Source text as legacy
  1. Add Confidence (optional but very useful)
  • confidence (decimal 0–1 or 0–100)
  1. 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 At is only set when resolution != open
  • Keep Value should 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).

regal hatch
#

its currently text and pooints to the json file from the nights serches brave_search_2026-03-02

pastel lionBOT
#

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:

  1. Keep Source exactly as-is (treat it as legacy_source_ref)
  2. 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 Value
  • source_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 least source_url)
  • keep Source as the raw “batch file reference” only

Watch Links additions

In Watch Links, make sure you have:

  • url (text)
  • retrieved_at
  • source_type
  • and add:
    • archived_copy_uri (text nullable) = your JSON file path like brave_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.

regal hatch
#

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?

pastel lionBOT
#

Yes — given you already have query + full results[], it’s absolutely worth normalizing. The clean pattern is a 3-layer pipeline:

  1. Raw search runs (what query ran, for which watch/field, when, did it succeed)
  2. Raw search results (each URL/snippet/rank returned)
  3. 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.py
  • external_search_id (text nullable) — your search_id
  • watch_id (link to Watches; nullable if some searches aren’t watch-specific)
  • field_name (text nullable) — your field
  • pt (text nullable) — your pt (“brand/model”, etc.)
  • manufacturer (text nullable)
  • model (text nullable)
  • query (text)
  • status (select: success | error)
  • result_count (integer)
  • executed_at (datetime) — your timestamp/queued_at
  • raw_json_uri (text) — file path like /tmp/watch_research_results.json
  • created_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..N
  • url (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 convenience
  • archived_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

  • id
  • watch_id
  • field_name
  • query
  • status (queued|running|done|error)
  • priority
  • queued_at
  • last_attempt_at
  • attempts
#
  • 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.Source as legacy text (raw file pointer)
  • Add Spec Conflicts.source_link_id (link → Watch Links)
  • Populate search_runs/search_results going 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:

#
  1. Do you want search_runs/search_results inside the Watch Database base, or in the shared Ops base?
  2. 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.
regal hatch
#

1 watch database, 2. 100-300

pastel lionBOT
#

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.

regal hatch
#

yes do these two things,

pastel lionBOT
#

Got it — the two things are:

  1. add a “normalized/ingested” marker so you don’t re-import the same searches forever
  2. 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”).

regal hatch
#

yes tell me the views to create

pastel lionBOT
#

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 (with executed_at, normalized_status, watch_id, field_name, query)
  • search_results (with search_run_id, rank, url)
  • Watch Links (with watch_id, url, retrieved_at/created_at, search_run_id, search_result_id)
  • Spec Conflicts (with Resolution, Detected At, Resolved At, and source_link_id link → 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_at is today
    Sort
#
  • executed_at desc
    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 = pending OR normalized_status = error
    Sort
  • executed_at desc
    Purpose
  • this is your “import is broken / stuck” alarm bell

3) Search Runs — By Watch (last 30 days)

Filter

  • executed_at within last 30 days
    Sort
  • watch_id asc then executed_at desc
#

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
    Sort
  • search_run_id asc then rank asc
    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_at is today (or created_at if that’s what you have)
    Sort
  • retrieved_at/created_at desc
    Purpose
  • “what evidence did Rosemary actually add today?”

6) Watch Links — Missing Field Support

Filter

  • fields_supported is 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
    Sort
  • Detected At desc
#

Purpose

  • main “things to review” queue

9) Spec Conflicts — Open (Missing Citation)

Filter

  • Resolution = open
  • AND source_link_id is empty/null
    Sort
  • Detected At desc
    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 At is not empty
    Sort
#
  • Resolved At desc
    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:

  1. links_count = rollup count of Watch Links
  2. open_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 OR links_count = 0
    Sort
  • open_conflicts_count desc then links_count asc

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.

regal hatch
#

currently it is a ID code in text, PT-097, but I realize that we really need a UID for every watch

pastel lionBOT
#

Yep — you want two identifiers:

  1. a true UID / primary key (stable, never changes, used for all links/joins)
  2. 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 id integer_auto primary key (or add a watch_uid UUID if you prefer UUIDs)
  • Keep PT-097 as a separate column:
    • watch_code (text, unique) = PT-097

So:

  • UID = Watches.id (or watch_uid)
  • Human code = Watches.watch_code

How to migrate without breaking anything

Because Spec Conflicts.Watch ID is currently text:

#
  1. Do not delete it yet. Rename it mentally to watch_code_legacy.
  2. Add a new column in Spec Conflicts:
    • watch = Link to Watches (this becomes the real FK)
  3. Backfill Spec Conflicts.watch by matching:
    • Spec Conflicts.Watch ID (PT-097) → Watches.watch_code
  4. 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 watch link if it isn’t already)
  • Watch Links (add watch link)
  • Service History (add watch link)
  • search_runs (add watch link; keep pt/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.

regal hatch
#

yes please write that prompt

pastel lionBOT
#
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.

regal hatch
#

Text: Id1

pastel lionBOT
#

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>).

regal hatch
#

Watch ID

pastel lionBOT
#

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.

regal hatch
#

✅ 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
#
  1. 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.”
pastel lionBOT
#

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_review sorted by last_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.

regal hatch
#

yes that is good, update the prompt to add the review inbox and the extractoion rules

pastel lionBOT
#

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.

regal hatch
#

nicknames are, Red House, A Frame, Gray House, 12W72 (or Home or Apartment), and Great Jones (rental apartment)

pastel lionBOT
#

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)
    or
  • property_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)

  1. Build a dictionary of aliases → property_id (from properties.nickname + properties.aliases).
  2. 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”
  1. 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.

regal hatch
#

yes please add that document folder path

pastel lionBOT
#

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_locator looks 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”
  • Partial/variant match in a folder name = +3
    • e.g. folder Aframe matches alias “A Frame”
  • 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.
#
  • 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.

regal hatch
#

more aliases: Red House is also 133 Woods, Gray house is also 127 Woods, A frame is also 125 Woods, and 127A Woods

pastel lionBOT
#

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 Ln if 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:
    1. a separate property record, or
#
  1. 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.

regal hatch
#

127A woods is the legacy name for 125 Woods. It was changed by the city.

pastel lionBOT
#

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.

regal hatch
#

yes add that, and then rewrite the exection prompt and this name normalizer into one prompt to execute together

pastel lionBOT
#
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.”

regal hatch
#

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.

pastel lionBOT
#

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:

  1. the Juniper prompt to create/populate the agent_runbooks table (with rows for Hazel/Rosemary/Holly/Olive/Judy), and
  2. a short per-agent “runbook seed” for each (so you’re not pasting huge prompts into system messages).
regal hatch
#

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?

pastel lionBOT
#

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:

  1. a Juniper prompt to create/populate the agent_runbooks table in your Ops base
  2. 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.
regal hatch
#

yes please write these, so I can reuse them

pastel lionBOT
#

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).

regal hatch
#

is property_documents table supposed to be in the Ops table?

pastel lionBOT
#

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.

regal hatch
#

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

pastel lionBOT
#
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.

regal hatch
#

yes, please make those first run packets that I can use as a template for others

pastel lionBOT
#

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).

regal hatch
#

yes plese writt hat

pastel lionBOT
#
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.

regal hatch
#

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?

pastel lionBOT
#

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_reports row(s) for that work
#
  • 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.”

regal hatch
#

yes, please do this.

pastel lionBOT
#
========================
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.”

regal hatch
#

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?

pastel lionBOT
#

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.

regal hatch
#

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?

pastel lionBOT
#

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.

regal hatch
#

yesm please do, where will thaT TABLE LIVE?

pastel lionBOT
#

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”).

regal hatch
#

YES, PLEASE DO. also the prompt to judy to use it and know where it is

pastel lionBOT
#

Here are two copy/paste prompts:

  1. Juniper: create the compliance_audits table + views in the Ops base
  2. 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.

regal hatch
#

yes, should that go in her runbook?

pastel lionBOT
#

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:

  1. the deprecation notice (old ACT scoring deprecated)
  2. her new mission (compliance + evidence auditor)
  3. the universal rubric
  4. 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.

regal hatch
#

can you write the prompt for Judy's runbook, and her reminder?

pastel lionBOT
#

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.
regal hatch
pastel lionBOT
#

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:

  1. Judy runbook (canonical):
    Ops base → agent_runbooks → agent=Judy → paste the full runbook text there.

  2. 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)
regal hatch
#

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

pastel lionBOT
#

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 outputs
  • agent_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”

  1. RunReport + Heartbeat Contract (every run logs to ops_run_reports)
  2. 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.

regal hatch
#

how often shoudl judy run her audit?

pastel lionBOT
#

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 blocked or stale runs in ops_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.

regal hatch
#

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.

pastel lionBOT
#

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).

regal hatch
#

Yes. Please generate the followup scope proposal for the ongoing process of processing every reel and writing all the data into the Reel database

pastel lionBOT
#
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).

regal hatch
#

Yes please generate it.

pastel lionBOT
#
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.

regal hatch
#

Yes. Who do I give rhe view spec to? Juniper or olive?

pastel lionBOT
#

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.

regal hatch
#

Can you write the prompt for juniper to implement the spec and install it into run book?

pastel lionBOT
#
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).

regal hatch
#

Yes please provide that text

pastel lionBOT
#
========================
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.

regal hatch
#

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:

  1. "Reel Assets — Ready" (your work queue)
    • Filter: status = new AND source_locator not empty
    • Sort: priority DESC, created_at ASC
  2. "Reel Assets — Processing" (monitor in-progress)
    • Filter: status = processing
    • Sort: last_processed_at ASC
  3. "Reel Assets — Needs Review" (completed, awaiting approval)
    • Filter: status = needs_review
    • Sort: last_processed_at DESC
  4. "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:

  1. status (enum: new | processing | processed | needs_review | error)
  2. priority (integer, for queue ordering)
  3. next_segment_start_s (progress marker for resumption)
  4. segment_duration_s (per-reel override, default 300s)
  5. last_processed_at (timestamp for monitoring)
  6. last_error (error message storage)
  7. source_locator (video file path)
  8. immich_asset_id (Immich reference)
  9. duration_seconds (reel duration)

All migrations are additive only (no destructive changes). Indexes added for performance.

───

Next steps:

  1. Copy VIEW SPECS into NocoDB UI → create the 4 views (15 min)
  2. Run SQL MIGRATIONS on Reels DB (5 min)
  3. Verify "Reel Assets — Ready" shows queued reels

Then Olive can run Phase 2! 🚀

pastel lionBOT
#

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_locator not empty OR immich_asset_id not empty)

Everything else looks good:

  • Ready sort: priority DESC, created_at ASC ✅ (if created_at exists)
  • 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 status a 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_at timestamp 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.

regal hatch
#

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

pastel lionBOT
#

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.

regal hatch
#

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.

pastel lionBOT
#

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_reports
  • agent_runbooks
  • compliance_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_path
  • owner_service (link → ops_services)
  • enabled (boolean)
  • last_loaded_at
  • notes

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_reports row 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)

  1. Juniper creates the 3 tables above in Ops base and seeds initial rows (Immich, NocoDB, Postgres, Ollama, OpenClaw gateway, etc.)
  2. Hazel gets a “Daily Ops Maintenance” Task Packet (bounded, reports to ops_run_reports)
  3. Hazel gets a “Weekly Ops Maintenance” Task Packet (cleanup + audit)

If you want, I can generate three copy/paste prompts next:

  1. Juniper prompt to create the 3 ops tables + seed rows + views (“Services Down”, “Jobs Failing”, “LaunchAgents Missing”)
  2. Hazel runbook text (complete)
  3. 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.

regal hatch
#

yes geenrate the three prompts, and make sure you tell her to stop following the old instructions for the ACT protocol.

pastel lionBOT
#

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.

regal hatch
#

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

pastel lionBOT
#

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:

  1. Websites — Persistent (Should Be Up)
  • Filter: persistence=persistent AND desired_state=up AND deprecated=false
  1. 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.)
  1. Websites — Deprecated But Still Up
  • Filter: deprecated=true AND actual_state=up
    (This is Hazel’s “shut down old stuff” queue.)
  1. 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_websites for host+port availability.
  • If a site is “ephemeral”: register it but set persistence=ephemeral and desired_state=down by default.
  • If a site should persist: set persistence=persistent + desired_state=up and add a real healthcheck_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.”

regal hatch
#

yes please generate these prompts.

pastel lionBOT
#

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).

regal hatch
#

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?

pastel lionBOT
#

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_ref that 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_jobs rows (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, updates ops_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.

regal hatch
#

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)

pastel lionBOT
#

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) with owner_agent=Hazel
  • ops_websites (HTTP endpoints) linked back via service_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.nocodb
  • launchd:com.machinefiction.reels-admin
  • launchd:com.machinefiction.mqtt

OrbStack (your ~7)

  • startup_ref: orbstack:<container_name>
  • shutdown_ref: orbstack:<container_name>

Examples:

  • orbstack:immich
  • orbstack: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-prod
  • ops-immich-prod
  • ops-openwebui-prod
#
  • reels-admin-dev
  • watch-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=true only 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 its service_ref.

5) Handling “most are deprecated”

Make it explicit so Hazel can clean them up safely:

  • deprecated=true
  • desired_state=down
  • deprecated_reason filled
  • (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.

fierce forge
#

[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:

  1. Criar subagent com sessions_spawn
  2. Executar tarefa simples
  3. Verificar que resposta nĂŁo chega

Expected: Resposta do agent no chat
Actual: Sem resposta, mas status = "done"

pastel lionBOT
#

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:

SugestĂŁo prĂĄtica:

  1. 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).
  2. 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.).

regal hatch
#

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)

pastel lionBOT
#

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).

regal hatch
#

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).

pastel lionBOT
#

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) on ops_services or ops_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=up and 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_services rows for each label/container
  • ops_websites rows 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.
regal hatch
#

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

pastel lionBOT
#
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.

regal hatch
#

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?)

pastel lionBOT
#

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, phillip
  • tailscale_ip
  • tailscale_fqdn (e.g. chekov.tail39fdfd.ts.net)
  • status_hint (optional: online/offline/unknown)
  • notes
  • deprecated (for old entries like chekov-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 set exposure=local_only
    (Optionally store the Tailscale URL in notes, or add a tailscale_url column.)

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”.

regal hatch
#

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?

pastel lionBOT
#

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 local http://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).

regal hatch
#

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 ✅'

pastel lionBOT
#

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=up
    • healthcheck_url = that same base URL (so Hazel can validate it)

Until then, keep:

  • can_auto_restart=false for 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”).

regal hatch
#

Also, how do we manage cron jobs that happen every minute or 15 minutes or hour? Are those being tracked by Hazel?

pastel lionBOT
#

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.)
  • enabled
  • failure_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_reports row 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_jobs row fields:
    • last_started_at
    • last_success_at
    • last_status (success/fail)
    • consecutive_failures
    • last_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_jobs as the registry
  • ensure each frequent job updates last_success_at reliably
#
  • 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

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.

regal hatch
#

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.

pastel lionBOT
#
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
regal hatch
#

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?

pastel lionBOT
#

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.

regal hatch
#

yes please do, are those rewrites of the invocation prompts, or additive to them?

pastel lionBOT
#

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.

regal hatch
#

the invocation prompts are things I need to cut and paste when I need them, or are they juniper's?

pastel lionBOT
#

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 a prompt_text field)
  • 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.

regal hatch
#

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?

pastel lionBOT
#

Here are two prepend blocks Juniper can paste on top of:

  1. every invocation prompt (the tiny “run it now” triggers)
  2. 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.

regal hatch
#

are these are stored in the runbooks of juniper and hazel?

pastel lionBOT
#

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:

  1. 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.
  1. Create a tiny table in the Ops base like ops_templates (cleanest)
    Rows like:
  • invocation_header
  • task_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_templates in the Ops base.
regal hatch
#

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?

pastel lionBOT
#

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_header
  • task_packet_header
  • scope_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.”

regal hatch
#

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?

pastel lionBOT
#

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:

  1. Invocation (trigger)
  2. Scope Ticket (one variable + quota)
  3. Task Packet (full instructions)
  4. Runbook (durable per-agent operating manual)
    …and the runtime log is ops_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.

regal hatch
#

yes, write it as a prompt for me to give juniper.

pastel lionBOT
#
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.”

regal hatch
#

yes please write it as an addendum to the prompt you just wrote

pastel lionBOT
#
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
========================
regal hatch
#

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?

pastel lionBOT
#

Below is a single bundle you can hand to Juniper: for Hazel Daily Ops Maintenance and Hazel Weekly Ops Maintenance, each includes:

  1. Invocation (with Invocation Header)
  2. Scope Ticket (one scope variable + quota)
  3. 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).

regal hatch
#

Do I need to update Juniper with with a complete ops template package like this one for hazel for each of the other agents?

pastel lionBOT
#

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.

regal hatch
#

OK, can you write that update prompt for me?

pastel lionBOT
#
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
regal hatch
#

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?

pastel lionBOT
#

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.

regal hatch
#

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.

pastel lionBOT
#

That’s a really clean state. You’ve got the separation exactly right now:

  • ops_templates = global boilerplate only
  • ops_job_packets = recurring jobs (full text lives here)
  • runbooks = thin, reference packets
  • ops_scheduled_jobs points to packet names

Immediate next steps I’d do (in order):

  1. Promote the 3 placeholder packets to full text

    • olive_reels_segment_batch_v1
    • rosemary_watch_enrichment_v1
    • holly_property_index_v1
      (You already have the content from earlier; just paste it in and mark them “full text”.)
  2. Add/confirm each packet has a real invocation_text

    • so Juniper (or a scheduler) can trigger without editing
#
  1. Once Olive/Rosemary/Holly packets are full, add them to ops_scheduled_jobs only 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.

regal hatch
#

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?

pastel lionBOT
#

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.

regal hatch
#

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

pastel lionBOT
#

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)

  1. Watches — Missing Sources (Queue)
  • Purpose: pick watches that need evidence first
  • Filter: links_count = 0 (or “no Watch Links rows”)
  • Sort: watch_code asc (or id asc)
  1. Watches — Needs Enrichment (Queue) (optional but useful)
  • Filter: enrichment_status in (new, in_progress)
  • Sort: last_enriched_at asc (nulls first)
  1. Watches — Has Open Conflicts (Queue)
#
  • Filter: there exists Spec Conflicts where Resolution = open
  • Sort: open_conflicts_count desc, then last_enriched_at asc

Run monitoring (what happened today)

  1. Search Runs — Today
  • Filter: executed_at is today
  • Sort: executed_at desc
  1. Watch Links — Created Today
  • Filter: retrieved_at is today (or created_at today)
  • Sort: retrieved_at desc

Review (human inbox)

  1. Spec Conflicts — Open (Review Inbox)
  • Filter: Resolution = open
  • Sort: Detected At desc
#
  1. Spec Conflicts — Open Missing Citation
  • Filter: Resolution = open AND source_link_id is empty
  • Sort: Detected At desc
  1. 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)

  1. Docs — Unprocessed (Queue)
  • Table: property_documents
#
  • Filter: status in (new, needs_review) (or just new, if you prefer)
  • Sort: created_at asc (or id 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)

  1. 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)

  1. Issues — Open
  • Filter: status = open
  • Sort: priority desc, then reported_date desc
  1. 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)’”).

regal hatch
#

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)

pastel lionBOT
#

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.

regal hatch
#

yes add olive's shortcuts.

pastel lionBOT
#

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.

regal hatch
#

that is the name

pastel lionBOT
#

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).
regal hatch
#

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.

pastel lionBOT
#

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:

  1. open the Ops base
  2. read its runbook + the council cheat sheet
  3. find the active job packets
  4. 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.

regal hatch
#

, yes give me one liners also, I will use them sparingly

pastel lionBOT
#

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.
regal hatch
#

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?

pastel lionBOT
#

You’re running into two very normal “first time NocoDB schema” friction points:

  1. queues that depend on rollups (links_count, open_conflicts_count)
  2. “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.watch is a Link to Watches (not text)
  • Then in Watches, add a Rollup field:
    • Name: links_count
#
  • Relation: the linked Watch Links relationship
    • Function: COUNT

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 Links is 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_count rollup (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

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_id to Spec Conflicts as a Link to Watch Links (nullable)
  • Keep legacy Source text 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)

  1. Ensure Watch Links.watch is a Link to Watches
  2. Add Watches.links_count rollup (COUNT of related Watch Links)
  3. Add Spec Conflicts.source_link_id (Link → Watch Links)
  4. 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.
regal hatch
#

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

pastel lionBOT
#

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.watch from text → Link to Watches
  • (Optional) keep a separate text field watch_code if you want the PT-097 visible without clicking, but it’s not required.

Spec Conflicts

  • Change Spec Conflicts.watch from 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 Resolution a Single Select with values:
    • open, accepted, rejected, needs_review
  • Set default = open for 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 Links
  • conflicts_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_id from 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 Watches
  • Spec Conflicts.watch → Link to Watches
  • Spec Conflicts.source_link_id → Link to Watch Links
  • Spec Conflicts.Resolution → Single select with default open

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).

regal hatch
#

YEs please write the exact prompt to make these changes

pastel lionBOT
#
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.
regal hatch
#

I also noticed that Watch Specs and Watches also only have a text Watch ID. Do wwe need to modify those tables also >

pastel lionBOT
#

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

  • Watches has the real UID (id) + a human code (watch_code, e.g. PT‑097)
  • Watch Specs is 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 (or Watches.Id1 if 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).

regal hatch
#

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

pastel lionBOT
#

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:

  1. Add a watch_uuid to Watches (stable external ID)
  2. Add a watch link-to-record field to every watch-related table
  3. Run a one-time backfill that links records by matching legacy text (Id1 / Watch ID)
  4. Optionally add watch_uuid as 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
regal hatch
#

what is the convenience>

pastel lionBOT
#

“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 from watch → Watches.Id1 or Watches.watch_code)
    • watch_uuid (Lookup from watch → 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).

regal hatch
#

Can you redact all mentions of addresses in this thread?

hazy elm
#

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