HYBRID WORK POLICY MAKER & SCHEDULER

Plan who comes in,
and when — intelligently.

A fully browser-native enterprise scheduling engine. No server, no cloud, no consultant required. Drop in your teams, set your rules, and let the solver find the best week that fits everyone.

🧠 Simulated Annealing ⚡ Web Workers (Parallel) 🎬 Live Solver Theater 📊 Trust Score 🔗 Synergy Matrix 📁 Formatted Excel I/O 💾 Sessions & Snapshots ✨ Optional AI Assistance
→

15 scattered team constraints → one balanced weekly plan

OVERVIEW

What is the Hybrid Scheduler?

The Hybrid Work Policy Maker and Scheduler is a self-contained, browser-based optimization tool built for corporate real estate planners, HR leaders, and workplace strategists. It answers one high-stakes question: given our seat count, our teams, and how those teams need to collaborate — what is the best weekly office schedule?

There is no backend, no database, no login. Everything — solver, UI, export — runs entirely inside a single HTML file in your browser. You can close the tab and nothing is lost on a remote server, because nothing was ever sent to one.

CORE PURPOSE

Match teams to office days so that synergy pairs share time, seat capacity is never exceeded, the weekly load is as smooth as possible, and every team's minimum office days are honored to the degree you've asked for — guaranteed if you marked it Must, a strong target the solver tries hard to hit if you marked it Should — all at once, automatically.

Who is it built for?

Corporate real estate teams negotiating headcount vs. floor space. HR leaders rolling out hybrid policy. Workplace consultants building scenarios for clients. Anyone who has tried to schedule 12+ teams with competing calendar needs in a spreadsheet and lost their mind.

WHAT'S NEW IN v2

The zone/neighborhood system from v1 has been removed entirely — it added complexity that most orgs never needed. In its place: a leaner solver that no longer over-schedules office days, a live view of the search actually converging, save/restore sessions, a properly formatted Excel round-trip, and optional AI assistance that never runs unless you explicitly turn it on and supply your own API key.

ARCHITECTURE

How is it built?

The entire application lives in one .html file. There are no frameworks, no build steps, no server. External dependencies are a handful of CDN libraries — ExcelJS for formatted spreadsheet writing, SheetJS (XLSX) for reading uploads, jsPDF / html2canvas / html2pdf for report exports. The solver itself runs in parallel JavaScript Web Workers — browser-native threads — so the UI never freezes during heavy computation, and you can watch it work in real time.

SYSTEM ARCHITECTURE
🖊
Input
Teams, seats, synergy rules — typed, matrix-clicked, Excel-imported, or spoken and AI-described
⚙️
Resolve
Build rule graph, determine directional vs. symmetric synergy pairs, validate feasibility
✨
AI Seed (optional)
If AI is on: one call proposes 2–3 starting schedules before local search begins
🔀
Spawn Workers
2–6 parallel Web Workers, each running independent solver restarts
🎬
Solve — Live
Simulated annealing + mutation, streamed to an on-screen solver theater as it converges
📊
Group & Display
Distinct solutions grouped with variations, scored, rendered in dashboard

The number of workers scales with your CPU: min(max(2, hardwareConcurrency − 1), 6). Iteration depth scales with team and synergy count. A small scenario (4 teams) might run 18,000 iterations; a large one (20 teams, complex synergy) may run 100,000+. AI seeding, when enabled, only ever adds one API call before the run starts — the actual search always stays local, so a slow or unavailable AI provider degrades gracefully to random restarts rather than blocking the solve.

CORE MODULES

Six building blocks

The tool is organized into six cooperating modules. Each is a distinct step in building your policy, and together they feed the solver everything it needs. The zone/neighborhood module from v1 is gone — AI Assistance takes its place as the sixth, and it's entirely optional.

ModuleWhat You DoWhat It Feeds
Scenario Setup Name the plan, set total seats, choose weeks to display (1–8). Calendar start date moved to the results panel — see below. Seat cap, weeks-to-render for the calendar view
Team Builder Add each team with headcount, exempt %, and minimum required office days — each minimum carries its own Must / Should priority, defaulting to Should. Or load one of 3 starting presets (Small / Growing / Enterprise) and edit from there. Effective headcount per team, and each team's real attendance floor (see callout below)
Synergy Matrix Set a global default (count × cadence × criticality), then fine-tune individual pairs in the matrix, via linear Excel rows, or via the Excel matrix-code sheet The rule graph the solver scores against
AI Assistance (optional) Switch on, connect your own API key (Anthropic / Gemini / Grok), then optionally describe your org in plain English to auto-populate teams and rules Auto-filled teams/synergy, seeded solver starting points, post-run explanations
Solver Engine Click Run — the solver does the rest. Watch it converge live in the solver theater. Distinct candidate solutions, grouped with near-identical variations, full evaluation metadata
Results Dashboard Read executive KPIs, explore charts, view the calendar, download reports Excel (.xlsx), CSV, PDF exports from any tab
ABOUT EXEMPT %

The Exempt % field accounts for employees who are permanently remote, part-time, or otherwise never come to the office. The solver works from Effective Headcount = Headcount × (1 − Exempt%), so seat math is always realistic.

MINIMUM DAYS NOW HAS A REAL PRIORITY — MUST vs. SHOULD

Every team's Minimum Days field now carries its own priority, exactly like a synergy rule does — Must or Should, defaulting to Should. This closes a real inconsistency: previously, a stated minimum was more rigid than even a Must synergy rule — the solver was structurally incapable of ever generating a schedule below it, no matter what else was at stake. That's backwards from how "Must" and "Should" read everywhere else in the app.

Must behaves exactly as Minimum Days always has — an unconditional floor. The solver cannot generate a candidate below it, full stop.

Should (the default) is a strong target, not a guarantee. The solver tries hard to hit it and very rarely falls short — but it genuinely can, if giving up one day for that team produces a meaningfully better schedule overall (say, satisfying several other teams' rules that would otherwise be missed). A miss costs 90 points per short day — the same rate as a missed Should synergy rule — and never blocks a solution from being called feasible, exactly like a missed Should synergy rule doesn't.

One thing that doesn't change regardless of which tier you pick: the solver still won't pad a team's schedule beyond its target without good reason — the existing excess-day penalty (120 points per day over) still applies. Priority controls whether the floor can be missed, not whether it can be exceeded.

The Matrix's diagonal cell (Team A × Team A) now shows this at a glance instead of sitting blank: a solid badge for Must, a dashed-outline badge for Should — deliberately styled to look like a fact versus an aim — and a note if a partner's Must synergy rule has silently raised the real floor above what you typed.

THE SOLVER

How the solver actually thinks

The solver is a stochastic optimization engine — meaning it explores the solution space through controlled randomness rather than brute force. It uses two techniques in combination: random mutation and simulated annealing.

What is a "solution"?

Each solution is a bitmask array — one 5-bit number per team, where each bit represents a weekday (Mon–Fri). A 1 on bit 2 means that team is in on Wednesday. The solver generates, mutates, and scores billions of these arrays to find the best.

SIMULATED ANNEALING — SCORE IMPROVEMENT OVER ITERATIONS
Early (high temp — accepts worse solutions) Late (low temp — only accepts improvements)
TEMPERATURE
COOLING

The four mutation types

Each iteration, the solver picks a random team and applies one of four mutations:

Toggle
MTWTF

Flip one day on/off. Fine-tuning.

Swap
MTWTF

One day off, another on. Same total.

Trim to floor
MTWTF

Remove a day the team doesn't need.

Reset
MTWTF

Brand new pattern. Escapes dead ends.

FIXED IN v2 — NO MORE DAY INFLATION

In v1, starting patterns were seeded with a random number of days above each team's minimum, and nothing pushed the search back down — so solutions routinely drifted toward everyone in the office most of the week. v2 fixes this in two places: every random start now begins at exactly the team's minimum (not a random number above it), and the scoring function directly penalizes any office day beyond what a Must-synergy rule or the stated minimum actually requires. The solver now only adds a day when it has no other way to satisfy a hard requirement.

✗ v1 behavior
MTWTF
Team needed 2 days — drifts toward 5
✓ v2 behavior
MTWTF
Team needed 2 days — stays at 2

Simulated annealing acceptance

Early in a run, the solver accepts even slightly worse solutions — this is the "high temperature" phase that lets it escape bad local optima. As iterations progress, the temperature cools and the solver becomes more conservative, only accepting improvements. This mirrors how metal cools from molten to crystallized — annealing to the global minimum of stress. You can watch this play out directly in the live solver theater that appears while a run is in progress: a team × day grid flips cells as better schedules are found, alongside a running best-score, feasibility flag, and per-lane activity dots.

WHY PARALLEL WORKERS?

Each Web Worker runs an independent random restart of the solver, starting from a different random seed (or, if AI Assistance is on, from an AI-proposed starting point). The main thread collects the best results from all workers. Results are grouped, not just de-duplicated — see Trust Score and Results Dashboard below for how the top options are organized.

Lane 1
searching…
Lane 2
searching…
Lane 3
searching…
Lane 4
searching…
SCORING

The scoring formula

Every candidate schedule gets one number. Higher wins. A clean solution — no overflow, no broken Musts — clears 100,000; every violation chips away from there.

// SOLVER OBJECTIVE FUNCTION
score = (feasible ? 100,000 : 0)
− overflow × 450 // seats exceeded
− mustMiss × 520 // Must synergy rule missed
− minDayMissMust × 450 // can't actually happen — structurally guaranteed
− minDayMissShould × 90 // v2.9 — Should-tier attendance target missed
− excessDays × 120 // an office day nobody asked for
− shouldMiss × 90 // Should synergy rule missed
− goodMiss × 40 // Good synergy rule missed
− smoothnessPenalty × 0.5 // day-to-day load variance
− peak × 0.5 // discourages tall peaks
Running Score 100,000

The gaps between these numbers are the point, not an accident. Overflow, a broken Must, and a broken Must-tier minimum all sit at 450–520 — to the solver, they're the same species of failure: non-negotiable. Every Should violation, whichever flavor, costs 90; every Good violation costs 40. Six Should misses would have to gang up before they'd outweigh one Must violation — that margin is what keeps "Must" meaning must, even while thousands of candidates are being compared per second. excessDays plays a narrower role: it's the tax on office days nobody actually asked for.

EXCESSDAYS NOW RECOGNISES A "MUST HUB" — FIXED IN v2.11

A team juggling several simultaneous Must relationships — say, one team that five other teams each independently need to overlap weekly — can genuinely need more office days than any single relationship implies, if those five partners don't naturally land on the same days. The old excessDays check didn't know that: it only looked at a team's single heaviest Must relationship, so it taxed that hub team's necessary extra days as if they were pointless padding. It's fixed now — for every candidate the solver actually evaluates, it checks which days are genuinely defending a live Must overlap in that specific schedule, and only charges the excess penalty on days that aren't. A team's real workload from being everyone's Must partner no longer looks like waste.

FEASIBILITY FLAG

Feasible requires overflow = 0, mustMiss = 0, and minDayMissMust = 0 — nothing else. A missed Should, attendance or synergy, never disqualifies a solution; it just costs points. Miss the cliff and you lose the entire 100,000-point bonus in one stroke, which is why the solver will always take a feasible plan over a beautifully-optimized infeasible one.

One set of numbers, two different jobs

Don't conflate this formula with the Trust Score below — related, but not the same machine. Every candidate produces one set of raw counts. Those counts feed two parallel calculations, grouped and weighted for two different audiences.

overflow mustMiss minDayMissMust minDayMissShould shouldMiss goodMiss excessDays smoothness peak
↘↙

RAW SOLVER SCORE — by severity

The search's private yardstick. Never shown to you.

Critical · 450–520 ptsoverflow, mustMiss, minDayMissMust
Preference · 90 ptsshouldMiss, minDayMissShould
Shape · ≤40 ptsgoodMiss, excessDays, smoothness, peak
→ one ranking number

TRUST SCORE — by category

The human-readable verdict, built after the search ends.

Seat Fit · 30%overflow
Synergy Fit · 25%mustMiss, shouldMiss, goodMiss
Attendance Fit · 20%minDayMissMust, minDayMissShould
Balance Fit · 25%peak vs. quietest day
→ four sub-scores, weighted & summed

The numbers differ on purpose: the left column is built to make the search flinch away from Must violations; the right column is built to explain a result to a person, so its internal penalties (coming up next) are far gentler. Notice the two attendance metrics feed exactly one sub-score each, on the right — same split, same idea, just translated for a human reader.

CONFIDENCE RATING

The Trust Score explained

The raw score is math for a machine. The Trust Score is the same judgment translated for a person — one 0–100 number, built from four named dimensions, each answering a different question about the same schedule.

TRUST SCORE — 4 DIMENSIONS
Seat Fit
30%
Zero overflow = 100. Each overflowed person: −2.
Does everyone fit in the room. The one dimension with no opinion — just physics.
Synergy Fit
25%
Must miss = −25, Should = −8, Good = −4.
How many promises to collaborate actually held, weighted by how much you meant each one.
Attendance Fit
20%
Should miss = −15. Must miss: can't happen.
Before v2.9 this number never moved. A dip now means one team's own floor lost to something the solver judged more important.
Balance Fit
25%
Peak minus quietest day, seat-normalised.
A week can satisfy every rule on paper and still feel chaotic to live through. This is the dimension that catches that.

OVERALL = (Seat×0.30) + (Synergy×0.25) + (Attendance×0.20) + (Balance×0.25)

These weights are the default, not a law — adjust them to fit your own priorities, and every solved schedule re-scores instantly from the same run. v1 carried a fifth dimension, NBD Fit, at 15%; when the zone system was removed, that weight moved wholesale into Balance rather than thinning across all four — balance is the closest living relative to what NBD Fit used to reward.

The Trust Score sits on every solution card, so you can read the trade-offs at a glance: one option may trade synergy for seat balance, another nails synergy but runs peakier. It's a compass, not a verdict — read the dimension that matters most to you, not just the headline number.

ADJUSTABLE WEIGHTS

30/25/20/25 is only the default. A weights panel under the solution buttons lets you set your own split — a space-constrained office might push Seat to 40; a collaboration-first culture might raise Synergy. Applying recalculates every solution from the same run, no re-solve needed, and re-groups the options since old ties may split and new ones may form.

Stringency: a guess before, a fact after

The sidebar's Synergy Stringency meter is honestly labelled pre-run — before solving, it's only a structural guess from the rules on paper. After every run, Realized Stringency replaces the guess with a measurement: peak seat use plus the share of rules that held with zero slack. One tells you what you asked for; the other tells you what it cost.

SYNERGY MATRIX

Two rule modes, four ways to fill them in

The Synergy Matrix defines how often and how urgently any two teams need to be in the office on the same day. Underneath, there are two data modes; on top of that, four separate ways to actually enter the data — every one of them writing to the exact same underlying rules, so nothing you do in one view can go stale in another.

SIMPLE (SYMMETRIC)

One rule for each pair. Team A ↔ Team B share the same frequency and priority. Best for flat orgs with symmetric relationships. Required if you want to fill synergy via the Excel matrix-code sheet.

DIRECTIONAL

A → B and B → A can have different rules. Useful when one team depends on another more than the reverse. The solver uses the stricter of the two. Excel-fillable only via the linear Synergy sheet.

Four ways to enter synergy data

In-app matrix — click through team pairs directly, with live tooltips explaining each priority level and a running constraint-density indicator. Headers show each team as A-Engineering, B-Product, and so on, so the letter code and the name are never ambiguous. With many teams the grid scrolls horizontally rather than squeezing columns unreadably thin — the first column stays pinned in place as you scroll, so you always know which team's row you're looking at.
In-app List Entry — a dedicated "List Entry" button sits right next to "Open Matrix," opening a fully separate modal that never touches the matrix grid at all — plain dropdown rows (Team A / Team B / Frequency / Priority / Remove), with an "+ Add override" control at the bottom, only ever showing pairs you've actually customised. A "Switch to Matrix" button is there if you want the grid instead, but nothing requires it. The same view is also available as a tab inside the Matrix modal itself, for when you're already looking at the grid and want to flip to the list without leaving. Both are exactly the same data — edit a value in either one and the other reflects it instantly, they're two windows onto one underlying list, not two copies of it.
Excel — linear rows — one row per team pair (Team A, Team B, Direction, Frequency, Priority). Works for any number of teams, and the only format that supports Directional mode.
Excel — matrix codes — a 20×20 grid mirroring the in-app matrix, filled with short codes like M1W (Must, 1×/week) or S2M (Should, 2×/month). Symmetric mode only. The sheet carries live conditional formatting: as you type a code, the cell colors itself by criticality — M turns red, S amber, G green — even in a blank template.

RECENCY COLORING — NEW IN v2.4

Every customised pair carries a small colored marker — vivid on whichever pair you touched most recently, fading toward gray the longer ago it was set — visible as a ring around the cell in the Matrix and a swatch in the List View, both reading the same underlying edit order. Pairs still following the Global Default carry no marker at all; there's nothing to flag. It's there for exactly one reason: when you're deep in a large matrix and need to double-check "did I actually set this one, or is it still on autopilot," the color tells you before you have to click in and check.

THE SIDEBAR SUMMARY IS NOW OVERRIDES-ONLY TOO

The live synergy summary next to the team list used to show the first few pairs regardless of whether they were customised or just following the default — with a large team count that list was mostly noise. It now leads with one line ("11 pairs follow the Global Default — Should · 1×/Week") and then lists only the pairs you've actually set, newest first, each with its recency swatch. Nothing is hidden; there's just nothing to show for a pair that has nothing special about it.

WHEN A WORKBOOK HAS BOTH FORMATS — AND WHAT COUNTS AS AN OVERRIDE

On upload, the app checks which synergy format actually contains data. Only one filled in → it imports silently. Both filled in → you get an explicit choice ("Use Matrix Input" vs "Use Synergy sheet") before anything is applied.

Since v2.4, import is also smarter about what actually counts as a customisation. The Setup sheet's "Global Default Priority/Frequency" fields are read first — if a Synergy row or Matrix code exactly matches that stated default, it's treated as confirming the default, not stored as a separate override, so the app's List View and summary stay just as clean whether your data came from the UI or a spreadsheet. Only genuinely different rows become explicit overrides. If a file doesn't declare a default at all, every row is imported as an override instead (nothing is silently guessed from the data), and the import summary tells you exactly that — so you know to set the default field next time if you want the shorter list.

Priority levels

Must — A hard constraint. Missing this shared day is a critical failure, penalized at 520 points per miss. The solver will sacrifice almost anything else to satisfy Must rules. Use for: legal, compliance, and leadership pairing requirements.

Should — A strong preference. Penalized at 90 points per miss. The solver strongly tries to satisfy but may trade off for seat fit or other Must rules. Use for: standing weekly syncs, cross-functional projects.

Good — A soft desirability. Penalized at 40 points per miss. Nice to have — the solver satisfies when it can without disrupting higher-priority constraints. Use for: ad-hoc collaboration, proximity benefits.

Setting frequency — count × cadence × criticality

New in v2: instead of picking from a flat frequency list, the global default (and the matrix short-codes) build a rule from three parts — how many shared days, how often that repeats (per week, per 2 weeks, per month, per quarter), and how strict it is. Everything converts to a weekly-equivalent number under the hood, since the solver only ever models one repeating week.

2 × per month × Should = 0.5 / week
WHY MUST IS CAPPED TO WEEKLY / BIWEEKLY

The solver has no concept of month-to-month rotation — it schedules one week and repeats it. A Should or Good rule at a monthly or quarterly cadence naturally becomes a small, gentle nudge once converted to a weekly-equivalent number, which is correct. But Must forces at least one full overlap day in every occurrence of that single repeating week — so a "Must, once a quarter" and a "Must, once a week" would solve identically, silently behaving like a weekly rule. To avoid that trap, the UI and the Excel matrix codes both cap Must to weekly or biweekly cadence. For anything less frequent, use Should instead.

THE MATRIX — FILLING IN LIVE

🔴 Must   🟠 Should   🟢 Good   — each cell is one team pair

OPTIONAL, OFF BY DEFAULT

AI Assistance

Everything described so far works with zero AI involvement — that remains the default. A master AI Assistance: Off/On switch in the sidebar controls whether any AI surface is even visible. Switching it on reveals a provider panel and five optional features, none of which run without you explicitly triggering them.

Provider & key

Paste an API key from Anthropic, Google Gemini, or xAI Grok. The provider is auto-detected from the key's prefix, the tool fetches your available model list from that provider directly, and you pick a model. The key is stored only in your browser's localStorage and is sent only to the provider you chose — never to us, never to any other provider.

WHAT AI DOES vs. WHAT IT DOESN'T
✓ Proposes, then gets out of the way
Your description AI (1 call) Teams + rules
AI seed idea Local solver Runs on its own
AI calls per run1
Time added~1–3 sec
✗ Never scores individual iterations
Iteration 1 AI call?
Iteration 2…40,000 AI call?
✗ TOO SLOW — NOT USED
Local iterations / sec~15,000+
AI calls / sec (if used)~0.5

An LLM call takes roughly a second or more; the local solver tests thousands of candidate schedules in that same second across parallel workers. Scoring every candidate through an API would turn a multi-second solve into an hours-long one — so AI's role is scoped to one proposal before the run, never a judge during it.

FeatureWhat It DoesWhen It Runs
AI Data Entry Talk, type, or upload audio to describe your org — the AI extracts teams, headcounts, seats, and a starter synergy matrix into a review preview, asks up to 5 follow-up questions for anything genuinely missing, and lets you refine (edit the text, dictate more, re-analyze) as many times as needed before applying. On demand — Apply only unlocks once the app's own completeness check passes (≥2 teams, every headcount set, seats present), then confirms before overwriting existing data
AI-Seeded Solver One call before the run proposes 2–3 promising starting schedules, which seed some of the worker restarts. The search itself still runs entirely locally. Automatic when AI is connected and you click Run; falls back silently to random restarts if the call fails
Explain This Solution A structured, decision-grade analysis (Context → What it does → Why the solver chose this shape → Impact per stakeholder → Pros → Cons → Watch-outs → Bottom line), grounded in the actual numbers — including the measured Realized Stringency, any same-score alternatives, and — since v2.11 — a named diagnosis when one team is a "Must hub" carrying noticeably more simultaneous Must relationships than the rest, which is very often the real reason it lands on more office days than its own stated minimum. On demand, per solution
Compare Solutions A holistic comparison of all distinct options: per-option profiles, explicit pairwise trade-offs with real numbers, a "winner by lens" (space efficiency / cohesion / flexibility), and one declared overall winner defended against the runner-up. When options tie on score but differ structurally, the comparison addresses that head-on. If a Must-hub team is shaping every option similarly, that's named once as shared context rather than treated as a flaw of any single option. A separate, lighter comparison covers near-identical variations within one group. On demand
Draft Communications A full drafting form: pick the audience (leadership / all staff / one specific team / HR & Ops), tick the topics to cover (announcement, rationale, FAQ, exceptions process, feedback ask), choose format (memo / email / executive brief / Slack post), tone, and length, and add custom instructions. Team-specific drafts cover only that team's own days. Everything is grounded in the selected schedule's real data and start date. On demand
RESPONSE LENGTH IS A GUIDE, NOT A GATE — UPDATED IN v2.11

An earlier version enforced length quite rigidly — a computed word count treated as a hard floor, with "never a single line" rules under every heading. That produced consistent depth, but it could also push the model toward padding when a scenario genuinely didn't call for much. It's softer now: the same adaptive calculation (team count, active rules, number of solutions being compared) still sets a sensible reference range and a token budget, but the model is told plainly to let the actual content decide — shorter when there's little to say, longer when there's real nuance, no padding either way. The token ceiling itself was also raised to give real headroom rather than a tight wall.

EVERY AI OUTPUT CARRIES A CAUTION NOTE

Any modal showing a genuine AI response — Explain, Compare, Draft Communications — now carries a persistent line: "AI-generated — it can be confidently wrong, miss context, or misstate a number. Treat this as a first draft and double-check anything you're about to act on." It doesn't appear on the (i) info-button explanations elsewhere in the app, since those are static, human-written text, not AI output — the note is reserved for content that actually came from a model.

RESPONSES NO LONGER FAIL SILENTLY — FIXED IN v2.12

Two real reliability gaps existed underneath every AI feature. First: nothing checked whether a response had actually finished or been cut off mid-sentence by hitting its token limit — a truncated answer just displayed as if it were complete. Second: if a provider returned genuinely empty content (a safety block, a dropped stream, an unusual response shape), the app rendered a blank modal with no explanation at all. Both are fixed now: a cut-off response triggers one automatic retry with more room before you ever see it, and only gets a visible "cut short" note if it's still incomplete after that; a truly empty response now throws a clear, specific error instead of silently rendering nothing.

The AI Data Entry loop, in detail

This is the deepest AI feature, so it's worth walking through: Capture — talk (a mic button using your browser's built-in speech recognition, no API key needed), type, or upload a recorded audio file, all landing in one editable transcript box. Analyze — one AI call extracts a structured preview (scenario, seats, teams, synergy rules) and lists up to 5 genuinely missing items as follow-up questions. Refine — answer the questions by editing the transcript or dictating more, then Re-Analyze; repeat as needed. Apply — once complete, one click populates the app with the usual overwrite confirmation and source badge.

THE APP DECIDES "COMPLETE," NOT THE AI

The Apply button is gated by a deterministic check the app runs itself — at least 2 teams, every team's headcount set, and seats present (either stated in your description or already set in the app) — independent of whatever the AI's questions say. This matters: an AI can be wrong about what's "enough," so the thing that actually unlocks Apply is a hard rule, not a judgment call. The AI's follow-up questions are a softer, helpful layer on top — for things like an unstated synergy frequency — that guide you toward a better result without blocking you from a technically valid one.

HONEST NOTE ON DATA FLOW

The core solver is, and remains, fully offline — nothing about the search itself ever leaves your browser. AI features are one exception to that: when you use them, your team names, headcounts, and synergy rules are sent to whichever provider you connected, using your own key, under that provider's own terms. Dictation adds a second, smaller exception — it uses your browser's built-in speech recognition, and in Chrome that means the audio is sent to Google for transcription, independent of whether AI Assistance is even switched on. If neither is something you want, stick to typing — the tool is fully functional without either, exactly as v1 was.

WHY AI DOESN'T SOLVE THE SCHEDULE ITSELF

An LLM call takes on the order of a second or more; the local solver runs tens of thousands of iterations across parallel workers in about the same time. Scoring every candidate through an API would turn a multi-second solve into an hours-long one, and LLMs are simply worse than local search at exact combinatorial optimization. AI's role is scoped deliberately narrow: propose a few good starting points, then get out of the way.

NEW IN v2

Sessions, snapshots, and knowing where your data came from

v1 had no save state at all — a refresh reset everything. v2 adds a full session layer, plus a visible indicator of which input method last touched your data.

SOURCE BADGE — CYCLES AS YOU WORK
Source: Manual
Autosave saved ✓
FeatureWhat It Does
Autosave Every change is saved to localStorage 500ms after you stop typing/clicking. Reopening the tool offers to restore it.
Save / Load Session Export the full scenario — teams, rules, results, selected solution, trust weights — to a .json file named ScenarioName_YYYY-MM-DD_HH-MM-SS.json, and reload it later or on another machine.
Snapshots Up to 10 named in-browser snapshots, so you can park a few scenario variants side by side without leaving the tool. Names default to ScenarioName_timestamp (editable before saving) so snapshots sort chronologically.
Source Badge A small live label next to the scenario name — Manual, Excel Import, AI Generated, or Preset (name) — showing exactly how the current inputs got there. The newest action always wins, and destructive actions (Excel import, AI generation, presets) confirm before overwriting.
Review-Before-Run Gate (new in v2.4) Whenever synergy arrived in bulk — Excel import, AI generation, or a preset — clicking Run Solver first asks you to confirm you've looked at it: "Review First" opens the matrix and holds the run; "Run Anyway" proceeds and won't ask again for that same batch. Hand-typed synergy never triggers this — you've already been looking at each value as you set it.
RESULTS DASHBOARD

Reading the output

After the solver completes, the Results Dashboard presents every structurally distinct solution found — not a fixed count of 5. Each has three analytical tabs (the fourth, Neighborhood Analysis, no longer exists since the zone system was removed).

TabWhat You'll Find
Executive Insights KPI cards (Trust Score, peak occupancy, total iterations), the Planned Occupancy block (eligible headcount, planned daily attendance, week person-days used vs. available, peak/trough days), an animated utilization chart with a real people-count y-axis and hover-for-team-breakdown, the per-solution Realized Stringency reading, sub-scores shown with their current weights, team attendance plan, deferred meeting flags, and a plain-English explainability panel naming exactly which team pairs are costing points and what single change would fix it.
Intelligence Graph Animated layered SVG chart with a labelled percentage y-axis — the utilization line draws itself in, markers fade in — showing % utilization, anchor density, and the all-together day flag (hoverable per day), team-by-team anchor pattern analysis, a Same-Score Alternatives block whenever another option ties this one's score with a different structure, and the constraint clash log. Peak day, anchor zone, and all-together day are called out in a caption strip below the chart rather than as floating labels on it, so they stay legible even when all three land on the same day.
Work Calendar A grid of every team × every weekday. Includes a Calendar Display Settings panel — new in v2 — where you set the start date (default: next Monday) after the run, instead of committing to it before solving.
FIXED IN v2.6 — CHART LABEL COLLISIONS

The Intelligence Graph used to place "Peak," "Anchor zone," and "All together" as separate floating text labels directly above the data points they described. When those three things happened to land on the same day — common, since the busiest collaboration day is often also the peak day — their labels piled directly on top of each other into an illegible mess, and the last day's value label could clip off the chart's right edge entirely. Both are fixed: the three call-outs now live in a single caption strip below the plot with guaranteed spacing regardless of which days coincide, and edge labels anchor inward instead of centering exactly on the boundary.

SOLUTION GROUPING

v1 always showed exactly 5 solutions, sometimes padding in near-identical schedules just to hit that number. v2 groups solutions instead: an option only gets its own entry if it's both a different score and structurally distinct (at least 3 day-swaps from every other option). Near-clones — same score, nearly the same schedule — fold into that entry as expandable variations, with the count shown in parentheses: "Option 1 — 95/100 (4)". A lighter, focused AI comparison is available just for those variations (when AI Assistance is on). You might see 2 distinct options in a simple scenario, or 5 in a complex one — whatever the search actually found.

Same score ≠ same schedule. Two options can tie on the weighted Trust Score while arranging teams completely differently. When that happens, the tool says so explicitly: an amber callout under "Why this solution ranks well" and a dedicated block in the Intelligence tab name exactly which teams sit on different days and how many day-swaps apart the options are — and the same facts are fed into the AI explain/compare prompts so the narrative addresses the tie head-on.

17 CANDIDATE SOLUTIONS → 3 DISTINCT GROUPS
EXPORT OPTIONS

Getting data out

Every results tab has per-tab export buttons. The tool supports three export formats, each with a distinct use case.

FormatBest ForContents
Excel (.xlsx) Sharing with ops/HR teams for further analysis. Stakeholder handoffs. Formatted, color-coded worksheets: Insights, Teams, Work Calendar, or Intelligence — plus a read-only colored Synergy Matrix view sheet on every export, mirroring what you see in the UI.
CSV Loading into BI tools, Google Sheets, or data pipelines. Flat tabular output matching the selected tab.
PDF — Per-Tab Export A quick capture of exactly what's on screen right now — one tab, one page (or a few, for a very tall calendar). The small PDF button under each results tab (Insights, Intelligence, Work Calendar). Captures that tab as-is, scaled to always fill the printed page's full width — never squeezed illegibly thin, never cut off sideways.
PDF — Executive Brief A one-page leadership handoff — the thing you actually print or attach to an email, not a data dump. KPI strip, Trust Score composition, a real vector utilization chart, a one-line synergy summary, a team snapshot, and 3-5 recommendations computed from this specific run — not template text. A second page comparing every distinct solution the solver found is appended automatically when AI Assistance is connected.
REBUILT IN v2.8 — FROM A SCREENSHOT TO A REAL DOCUMENT

The original PDF export was a full-page screenshot of whichever results tab happened to be open, stretched into an A4 page — blurry chart, browser-only styling that didn't belong in a printed document, no real structure. It's now purpose-built: everything on the page is drawn directly as vector text and shapes, so it stays sharp at any zoom, and the content is a genuine executive brief rather than a dump of every tab. It fits deliberately in one page when AI Assistance is off. When it's on, exporting also fires a single fresh AI call and appends a second page — the same one-call, no-pagination-guesswork approach used everywhere else the tool talks to a model. The recommendations on page one are never static text: they're computed from this run's actual peak day, average utilization, Must-rule misses, and Realized Stringency, so a quiet, well-balanced week and a maxed-out one get genuinely different advice.

THE AI PAGE COVERS ALL OPTIONS — UPDATED IN v2.11

It used to write about whichever single solution you had selected when you clicked export. It now compares every structurally distinct solution the solver found — the same options, character, and trade-offs the in-app Compare Solutions feature covers — so the AI page is a genuine leadership comparison document, not a narrative about one arbitrary pick. If the solver only found one distinct solution, it falls back to a single-solution explanation instead of comparing an option against nothing.

PER-TAB PDF EXPORT — COLUMNS NO LONGER GET CUT OFF, FIXED IN v2.12

The per-tab PDF button used to leave page-fitting to a generic screenshot-pagination library, which is exactly what was producing a wide Work Calendar split confusingly across multiple pages instead of scaled to fit. It's rebuilt now with the same philosophy as the Executive Brief: full manual control over the scaling math instead of trusting a library's heuristics. Every export scales to the printed page's full width first — so columns can never be cut off sideways — and automatically switches to landscape when the content is genuinely wider than tall. If a tab is still too long to fit one page at that width (a calendar with a lot of teams), it tiles straight down the page across as many pages as needed, always at full width, never sideways. Verified against a real 25-team calendar: every column stayed intact on both resulting pages.

The Excel round-trip — rebuilt in v2

Download an empty template (or export your exact current state) as a fully formatted workbook: styled headers, frozen panes, zebra-striped rows, and a comment on every input cell explaining what it does and how it affects the solver. Five sheets: Instructions, Setup, Teams (now including a Min Days Priority column — Must or Should, dropdown-validated, defaulting to Should for older files that predate this column), Synergy (linear, dropdown-validated), and Matrix Input (the new 20×20 short-code grid described above). Exporting your current state also adds the read-only Synergy Matrix view sheet. The Matrix Input sheet colors codes by criticality as you type (conditional formatting baked into the file), and every export carries a sortable timestamped filename — the template as Hybrid Scheduler Tool_Input_Template- YYYY-MM-DD_HH-MM-SS.xlsx, and Export Current State as ScenarioName_YYYY-MM-DD_HH-MM-SS.xlsx. This is a real upgrade from v1's plain, uncommented cell dump — and the NBD/Team Rules sheets from v1 are gone along with the feature.

Instructions
Setup
Teams
Synergy
Matrix Input
CURRENT LIMITATIONS

What the tool doesn't do — yet

The Hybrid Scheduler is deliberately scoped. It solves one problem very well. Here is an honest accounting of its current boundaries.

📅
Fixed weekly patterns only
The solver assigns one pattern per team — a set of weekdays they repeat every week for the full configured period. It cannot handle biweekly rotations, alternating teams, or exceptions for specific calendar dates.
CURRENT LIMIT
👤
No individual-level scheduling
Teams are treated as atomic units. Every non-exempt person in a team follows the same days. Individual preferences, seniority-based flexibility, or sub-team splits are not modeled.
CURRENT LIMIT
🌐
No zone or multi-building modeling
v1's Neighborhood (NBD) system, which modeled floor/zone seat caps within a building, has been removed entirely — it added complexity most orgs didn't use. Seats are now a single flat pool. Organizations needing zone-level or multi-site capacity modeling aren't supported.
CURRENT LIMIT
📆
No calendar integration
The tool does not connect to Google Calendar, Outlook, or any HRMS. It generates schedules but does not push them to any system of record. Distribution requires manual export.
CURRENT LIMIT
📈
No historical data input
The tool works from manually entered rules (or an AI-assisted description of your org). It cannot ingest badge data, meeting records, or space utilization telemetry to auto-generate synergy rules or headcounts from observed behavior.
CURRENT LIMIT
🤝
No real-time collaboration
Sessions and snapshots live in one browser's local storage — multiple planners cannot co-edit a scenario simultaneously. Sharing means exporting a session .json or Excel file and sending it to someone else to load.
CURRENT LIMIT
🗓
Must rules can't model longer-than-biweekly cadence
Because the solver only ever models one repeating week, a Must rule at monthly or quarterly cadence would silently behave exactly like a weekly one. Must is capped to weekly/biweekly in both the UI and the Excel matrix codes; use Should for anything less frequent.
CURRENT LIMIT
🔑
AI features require your own key and leave the browser
The core tool is fully offline. AI Assistance is the one exception — when switched on, it sends team/synergy data to whichever provider (Anthropic, Gemini, or Grok) you connect, using your own API key, subject to that provider's terms. Leave AI off if that's not acceptable for your data.
CURRENT LIMIT
📐
Excel matrix input capped at 20 teams, Simple mode only
The matrix-code Excel sheet is a fixed 20×20 grid and only supports Symmetric synergy rules. Larger orgs or Directional relationships need the linear Synergy sheet instead.
CURRENT LIMIT
🎤
Dictation depends on your browser
Live voice dictation uses the browser's built-in speech engine: solid in Chrome and Edge, partial in Safari, unavailable in Firefox (the mic button hides itself automatically there). Typing always works everywhere as the fallback.
CURRENT LIMIT
📼
Audio-file transcription needs a Gemini key specifically
Of the three supported AI providers, only Google Gemini accepts audio input — Anthropic and Grok keys can't transcribe a file. Uploading with a non-Gemini key gets a clear explanation and a pointer to live Dictate instead, which needs no key at all. Files are capped around 15MB.
CURRENT LIMIT
WHAT'S NOT HERE YET

Potential future directions

These are capabilities that the architecture could support but have not been built — either because they require server infrastructure, external data sources, or significantly more complexity.

🔄
Rotating / biweekly schedules
Teams that alternate weeks (A/B scheduling), or patterns that shift every two weeks to spread load while maintaining overlap frequency over a longer horizon. This is also what would let Must rules finally support monthly/quarterly cadence properly.
FUTURE IDEA
📡
Live badge data integration
Pull real occupancy from access control systems to auto-populate headcounts and flag rooms that are consistently underused or overloaded, grounding the model in observed reality.
FUTURE IDEA
👥
Individual-level flex scheduling
Allow sub-team splits and individual exceptions. Engineers with childcare needs, part-time workers, and senior staff with different expectations could all be modeled explicitly.
FUTURE IDEA
🧩
Excel matrix input beyond 20 teams / Directional mode
A larger or dual-triangle grid to bring the matrix-code Excel workflow to bigger orgs and directional relationships, without the doubled cell count becoming unmanageable.
FUTURE IDEA
THE PHILOSOPHY

The tool is intentionally self-contained and offline-first. It was designed to be dropped into an enterprise environment with zero IT procurement — no SaaS contract, no vendor lock-in, no data leaving the organization by default. AI Assistance is the one deliberate, opt-in exception to that last point: switching it on and connecting your own key sends scenario data to the provider you chose, and only that provider. Everything else — the solver, the matrix, sessions, exports — stays exactly as offline as v1 always was.