20-person structural engineering studio · Operations & Marketing · May – September 2026

AI and Automation Work

One entry per project: what it does, when it was first built, every subsequent update, and current status. Grouped by the month a project started; the month filter matches any month a project was worked on.

45projects cataloged
24+BQE projects created from config
~1,200 → 10interactions per PM billing cycle
967InDesign links relinked (9.1 min)
2009–2026pursuit record coverage
17BQE rule documents
Month
Area
Software
Built with

Tap a button in any row to filter. Rows combine.

Status keyIn use: operating now, on a schedule or on demand. Complete: one-time work, finished, nothing ongoing. In progress: still being built or iterated. Not yet live: built and verified, not switched on. Awaiting PM input: blocked pending a decision.

May 2026

Late May

Staff project-tier dashboard

SharePoint · Power Automate · PnP List Formatting

Per-staff donut chart of project-tier mix (Tier 1/2/3), built natively on M365 Business Premium without Power BI Pro. Pipeline: Excel → source list → aggregation flow → stats list → List Formatting JSON view formatter → Lists web part. Alternatives evaluated and written down: Power Apps canvas app (kept as a fallback playbook), Excel embed, external hosting. This list + formatting-JSON architecture was reused for every dashboard built afterward.

Awaiting PM inputDonut verified live for a test case. Dashboard page and scheduled refresh flow not finished; direction on further dashboards pending PM input.

June 2026

Jun 11 →
ongoing

Monthly pre-billing worksheet

BQE · SharePoint · Excel · Graph

Each PM's <Month> AI worksheet is built from BQE open WIP and written into their SharePoint workbook, then verified by reading the sheet back and comparing row count and dollar total. PMs mark Bill? with a y, and the marked total drives the invoice batch. This is the most iterated system in the workspace — the layout, the totals, and the edge cases have been revised continuously since June as real billing months exposed them.

Updates

  • Jun 22 Which phases appear settled by status rather than by whether time is open: Active, Inactive and On Hold phases all show. Inactive means it will be billed later, On Hold means held — both belong on the sheet. Inactive FA/CA admin phases that have not started are dropped.
  • Jun 22 WIP corrected to exclude non-billable time, and a fully-billed fixed phase that still carries open time now reports as an error rather than being billed silently.
  • Jun 23 Row color encodes state so a PM can read the sheet at a glance: ready-to-bill fill, red for the error case, gray fill for On Hold, gray font for Inactive, banding for active rows. Placeholder fixed-fee contracts handled, since BQE requires a number on a fixed phase.
  • Jun 25 Five per-PM builder scripts replaced by one command covering all PMs (build, render, verify), designed to be re-run throughout the month as new time entries land.
  • Jun 25 Auto-filter kept disappearing; traced to the render step clearing the whole sheet range, and fixed.
  • Jul 6 Date cutoff added: only time and expenses through the tab's own month-end are counted, after a July entry turned up on a June sheet.
  • Jul 9 v2 layout rolled to four PMs after testing on one; one PM stayed on v1 for June, so layout versions coexist by design and each summary formula has to match its own tab.
  • Jul 9 Bill? column is free text — engineers type y to mark a phase for billing. A proper yes/no dropdown was attempted and rejected by the Graph API, the same limitation that blocks auto-filter, so the convention is a typed letter rather than a picker.
  • Jul 9 Marked total extended to include expenses, not just billable time. Active fixed phases now compute as percent complete times contract value.
  • Jul 9 Incident: re-deploying a layout onto a live sheet wiped 12 of a PM's y-marks. Recovered through SharePoint version history and a re-apply script matched on project and phase. Rule since: once a sheet is live, only the merge-preserving build touches it.
  • Jul 10 Phase-level PM support — a PM's sheet includes phases they manage under another PM's root, with those dollars counting toward their total. Notes column keeps each PM's manual note alongside the script's managed tag. Summary tab monthly total wired to the AI-tab total.
  • Jul 10 Rule recorded: do not render into a PM's sheet while they are working in it, since these are shared documents.
  • Jul 13 Retainer tag: projects with an open retainer carry a "Retainer to be returned" note so nothing is billed without accounting for it — 10 open at the time.
  • Jul 13 A grayed row grays its note too, so an inactive or held row reads as one thing rather than a gray row with a colored note.
  • Aug 10 Fully-billed-with-open-time detection corrected after a PM found a case it missed.
  • Aug 13 Retainers on the bottom line: "Less retainers" and "TOTAL after retainers" added below the total as live formulas, capped per project, because BQE's invoice-batch screen totals net of retainer credits and caps each credit at that project's bill. The gross total still feeds the summary; the net line is for reconciling against the batch.
  • Aug 13 Freeze script added to convert a billed month's summary column to static values before its live formulas collapse. Summary ranges extended after a PM's July total read low. Scripts renamed off the confusing "June AI" naming.
  • Aug 14 Rule recorded: manual cell highlights and cell comments are wiped on every rerun. Only the script's own colors carry meaning, so a stray highlighted cell is not a signal.
  • Aug 17 Discount column (v3): a project was invoiced after a discount while the sheet still showed its full WIP. A manual Discount column now sits after Non-billable, and the marked total subtracts it. Retro-fitted into an already-billed July by inserting the column in place rather than rebuilding; that month reconciled to the cent, matching both the frozen summary and the invoiced amount.
  • Aug 17 Remaining 2026 month tabs pre-created for every PM, each summary formula matching its tab's layout version.
  • Aug 25 Bill-through-month: when a PM bills late for a prior period, typing Bill hrs thru Jun in the row's Notes caps that project's WIP at that month-end so the sheet matches what was actually invoiced. The note is the source of truth, and a manager's tag is never overwritten.
  • Aug 26 Billed-tab freeze written up as a standing rule with a recovery procedure, after a refresh erased a billed month; the scheduled July refresh task was disabled so it cannot recur.
  • Sep 1 August cycle built for all five PMs, with open retainers refreshed and a per-PM snapshot taken so manual edits survive the rebuild.
  • Sep 9 Dropdowns, filters, frozen header and row highlighting now exist. Microsoft Graph has no endpoint for data validation, auto-filter, freeze panes or conditional formatting, which is why Bill? had been a typed letter since July. Four Office Scripts run inside Excel, where the full object model is available, and cover the template and every month tab so new months inherit them. The new row-highlight script replaced an old one that was hardcoded to June, used the retired 10-column range, and stacked a duplicate rule on every run.
  • Sep 9 Incident: two Office Scripts were deleted through Graph. Excel keeps its own script index rather than reading the folder, so the files vanished while the index kept them — scripts went missing from the list, a third disappeared with them, and recreating one by name was refused as already taken. Rule since: Office Scripts are managed in the Excel interface only; automation may read one to see its code but never deletes, renames, creates or modifies one. Recovery is a hard refresh of the workbook tab.
  • Sep 9 Billing Task column made build-owned and no longer preserved across rebuilds. It is derived from BQE, so a manager edit there is corruption rather than input. Hourly rows now read Hrly thru <Mon>.
  • Sep 10 v5 layout, 15 columns. Two retainer columns added: Ret Open?, written by the build as Y on a project's final phase row when BQE shows an open retainer balance, and Return Ret?, the PM's Y or N answer, preserved across rebuilds and cleared only once BQE shows the retainer settled. The four manager inputs — Discount, Bill?, Return Ret?, Notes — have colored headers so it is obvious where to type.
  • Sep 10 Defect: the preserve rule for Return Ret? keyed off its own freshly built value, which the build always writes blank, so every PM's answer was wiped on rebuild. It now keys off Ret Open?.
  • Sep 10 A Bill Through dropdown column was built and removed the same day. Zero rows used it across all five PMs in August, and a per-month cell for a project that is behind has to be re-entered every month and will be forgotten. Bill-through is a note again, now accepting Bill thru <Month> with flexible wording; column F reading Hrly thru Jul is the receipt that it applied. Proven on a live project whose WIP was capped at the noted month-end.
  • Sep 10 Sheet protection with per-cell unlocking was built and removed the same day, because a protected sheet rejects the Office Scripts that supply the dropdowns and filters. The colored input headers replaced it.
  • Sep 11 Report the L total, not the I total. The build's printed figure is the sum of every billable row — the ceiling of what could be billed. What is actually being billed is the Bill? column total: Y-marked billable plus expenses minus discounts, then the after-retainers line below it.
  • Sep 11 Double-billing caught on Active fixed phases. A fixed phase was Active, 100% complete, and already billed its full contract, yet the sheet showed the full contract billable again and the staged batch proposed more — 152.8% of contract. The billed-to-date check had only run for Completed fixed phases. It now runs for Active ones too, the row renders red as billed in full with the unbilled time still booked, and invoice drafting skips any Y-marked row reading "Billed in full." A second project was caught by the same check: two phases showing billable amounts that had already been invoiced.
  • Sep 11 An Active fixed phase already billed in full with no open time now reads plainly as "Billed in full" rather than red. Recorded as not yet handled: an Active fixed phase that was partially billed before still shows percent complete times contract, overstating by the prior billing.
  • Sep 9–11 August invoices drafted and sent for three PMs; September tab started for one.
5 PMs · layout v1 → v2 → v3 · nightly scheduled refresh · per-PM snapshots preserve manual edits across rebuilds

In progressIn production and used every month; still being iterated as new billing cases appear.

Jun 11 →
Aug 25

Past-due invoice reminder emails

BQE · Playwright · Chrome CDP

Sends one reminder email per project with overdue invoices, routed to the client contact or the client per BQE's own routing, with a permanent never-send list for accounts on hold. The first version reproduced the manual click path; it was rebuilt in July around the invoice grid's own API after DOM scrolling proved unreliable.

Updates

  • Jun 17 First run produced a written lessons list, including that the "preview before sending?" prompt sends immediately — recorded as a hard warning before any later attempt.
  • Jul 14 Rebuilt with three modes: preview by default, --test routing real mail to an internal address, and --live requiring explicit approval. One email per project regardless of invoice count, with duplicate-send guards.
  • Jul 22 Switched to reading the grid through its own API rather than scrolling the DOM, which had been returning only ~57 of 82 rows. Two dead ends documented so they would not be retried: the public API ignores the due-date clause, and captured grid filter JSON is session-bound.
  • Jul 22 ACH rule clarified — hold only for in-flight payments, never a completed one.
  • Aug 25 A saved filter left on the BQE grid was hiding overdue invoices: the grid showed 27 past-due when the true count was 42. The script now strips every grid filter except the as-of date and determines past-due in code.
42 true past-due invoices vs 27 shown by the filtered grid · preview / --test / --live tiers · never-send list honoured

In use

Jun 11 →
Aug 26

Project setup from config

BQE · REST API

A new BQE project is created from a single JSON config: root and phases, contract type and fee, budget lines, phase dates, active/inactive status, and activity codes — then the script verifies itself through the API. Roughly 24 projects have been stood up this way, replacing about 120–150 manual clicks each.

Updates

  • Jun One bespoke script per project (clone-and-swap). A date-field bug propagated through the copies, which is what motivated generalizing.
  • Jul Consolidated into one config-driven script; per-project configs became data files rather than code.
  • Aug 19 The firm's three standing project rules (joint invoice, skip auto time evaluation, skip auto expense evaluation) applied to every node of every project.
  • Aug 24 Overhead contract type added as a config flag, marking root and phases as no-fee for internal and at-cost work.
  • Aug 26 Budget rates now read live from each resource's employee record at write time. The public API does not auto-fill rates the way the web UI does, so every previously written budget line held a null rate.
  • Aug 26 An architect's stale client ID was corrected in the rules and in five earlier configs; a name-lookup fallback had been masking it.
  • Aug 31 QuickBooks push counted honestly. Both callers had measured success against the response rather than against what was sent, and BQE does not always return one record per id — one project pushed 8 nodes, got 5 records back, and printed "5/5 records linked." A clean success line for an incomplete push. The count is now taken against the ids sent, and the push logic is shared between the setup script and the standalone pusher so the two cannot drift apart.
  • Aug 31 Retry added for the inactive-parent ordering error, which QuickBooks raises when a parent is deactivated before its children.
  • Aug 31 Audit written to re-push every project built before that fix and report an honest per-project link count, since each of those carries a success message that proves nothing. Safe to re-run: the sync updates existing links rather than duplicating, adds only what is missing, and touches nothing but QuickBooks.

In use

Jun 11–15

Marketing status board

SharePoint · List Formatting · WordPress API

A SharePoint page for leadership showing where each marketing project stands: cards grouped by discipline (Architecture, Art, Fabrication), a Kanban board view, a goals roll-up view, and a home-page feed of new website projects pulled from the firm website's WordPress API into a SharePoint-hosted image library. Each entry names the current blocker — photo rights, architect coordination — and an owner. Rows are edited in place. SharePoint formatting limits found during the build (lane headers cannot be styled, grouped views allow only one card per row, multi-value fields render blank unless added to view fields) are documented in the project memory.

In useWeekly website-feed refresh flow and the collateral section were scoped but not built.

Jun 15 →
ongoing

Footage keyword tagging

Python · CLIP · DaVinci Resolve API

A local, offline tagger for project footage. Frames are extracted per clip and scored against a closed per-project keyword bank — the tool may only assign supplied vocabulary and never invents terms — and the result is written as a CSV that Resolve imports into the Keywords field, or applied directly through the scripting API. Nothing is uploaded.

On one project's S-Log3 stair footage, CLIP scores did not separate co-occurring stair parts: handrail, baluster, tread and stringer all landed in the same band, and "stringer" also matched rooftop shots with no staircase. Only chains and rigging had usable signal. That project's delivered tags were produced by visual review of the extracted frames against the same keyword bank. The frame extraction, CSV schema and Resolve import path remain in use.

Updates

  • Jun 4 Setup and requirements documented for cross-machine use, so a new machine can be brought up without re-deriving the environment.
  • Jun 15–17 Bulk frame extraction across two shoots (91 clips, 639 frames); CLIP calibration run; classification limits found and written up.
  • Jun 25 Keyword bank externalized to a per-project config with an individually tuned threshold per keyword, because no single global threshold works across concrete close-ups and relational scene terms.
  • Jul 7–13 Made cross-platform for Windows and macOS.
  • Aug 26 API media import blocked on Blackmagic Cloud projects, after import-as-local-reference put 34 clips offline in a collaborator's timeline. Bin creation and metadata remain available on cloud projects.
91 clips · 639 frames cached · closed vocabulary with per-keyword thresholds · Resolve replaces the Keywords field on import, so the tool always emits the complete set per clip

In progressPipeline in use; classification currently manual against the keyword bank while the model approach is reconsidered.

July 2026

Jul 7–13

Project activity feed

SharePoint · BQE API · Graph

A SharePoint widget showing each engineer their own active BQE projects (more than 5 hours in the last 30 days) alongside recent file activity in each project's calculations folder, personalized natively with SharePoint's [Me] filter rather than a custom app. Built with sequentially numbered scripts so the folder reads as its own build log. A custom card formatter was built, rejected as illegible, and reverted to a native table with person chips; the rejected formatter is kept in the repo and labeled as such.

Updates

  • Jul 8–13 Display reshaped to a grouped list with hours in the group header; one-off checkpoint scripts consolidated into a single unattended aggregator.
  • Jul 13 Registered as a scheduled task, first daily then weekly on Mondays.

In useWeekly Monday refresh.

Jul 7 →
Aug 28

Timeline Usage widget

Python · tkinter · DaVinci Resolve API

Resolve's Media Pool "Usage" column counts a clip's use across every timeline in a project, so it cannot answer whether a clip is in the cut currently open. This adds exactly that: a read-only floating window that polls the Resolve API about once a second and shows, for the current timeline, a used/unused status for the selected Media Pool clip — green for not used, cranberry for used — plus a scrolling list of every unused clip. It follows timeline switches and edits live. Clips are keyed by source file path so the same media duplicated across bins counts once; audio, stills, generators and compound clips are excluded unless --all is passed. A command-line version prints the same snapshot without the window. It changes nothing in Resolve.

Updates

  • Jul 7 Cross-platform paths for the Resolve scripting API on Windows, macOS and Linux, with a launcher for each machine.
  • Jul 13 macOS watcher added to auto-open the widget.
  • Aug 28 Verified running on Windows against a live project.
  • Aug 29 Audio-track usage counting added, with a VIDEO / AUDIO / BOTH filter.
  • Aug 30 That change reverted — the widget answers a video question, and counting audio tracks alongside it made the used/unused reading ambiguous rather than richer.
Verified Aug 28 on Resolve Studio 21.0.4.5 (cloud database): a live project timeline — 70 footage clips, 31 used, 39 unused; Media Pool selection read correctly. Works with media offline, reading stored path metadata rather than the media.

In useRuns on both Windows and Mac from the same repo.

Jul 9 →
Aug 17

Invoice creation and sending

BQE webapp endpoints · Chrome CDP

BQE's public API cannot create or send invoices, so the webapp's own endpoints were captured from recorded UI actions and replayed. Invoices are now created from the worksheet's y-marks and emailed from BQE, with a preflight check that aborts the run if BQE's UI changes rather than producing a wrong invoice.

Updates

  • Jul 9 Invoice send endpoint captured and scripted.
  • Jul 22 Preflight canary wired into the send and create workflows.
  • Aug 10 Invoice creation from worksheet y-marks automated, with preview, --stage and --process tiers; this closed the last manual gap in the monthly chain.
  • Aug 17 Delivery verification: 14 invoices reported "Email sent successfully" and were stamped with a sent date, but Exchange message tracing showed nothing was transmitted. Standing rule added — report "submitted," never "sent," until an independent receipt exists — and Graph message-trace checking joined the toolchain.
  • Sep 10 Incident: discount and retainer edits silently dropped. Percent-complete edits persist because BQE writes them back to the project record, but discount and retainer edits live only on the batch row. When the batch was processed through another path three minutes after the edit, both adjustments were lost: one invoice printed a few cents over its target, and another printed without its adjustment. Editing and processing in one uninterrupted run held — a third invoice came out exact. Rule since: never report an adjustment as applied from the batch grid; verify against the processed invoice record.
  • Sep 10 An exact invoice amount cannot come from percent complete, because BQE rounds it to two decimals when it calculates. On one project, a target amount billed a few cents under at 92.42424% and a few cents over at 92.43%. An exact figure has to come from a discount on the row.
  • Sep 10 Auto-apply retainer defaulted to off. It is a batch-wide checkbox that hits every project in the batch carrying a balance, and it is only correct when a retainer is actually being returned.
  • Sep 10 Every sent invoice now copies the operator, per standing rule.
  • Sep 11 Return Ret? drives the retainer. If any staged project is marked Y, the batch is created with auto-apply on; because the flag is batch-wide, staging stops if another project in the batch has an open retainer not marked Y, so those are staged separately. On the first run, the staged amount was the bill less its retainer, and the batch total matched the sheet's after-retainers line. Proven through processing: the invoice records the full bill, with the retainer appearing as a balance reduction rather than in the invoice amount.
  • Sep 10 Recorded: deleting an invoice releases its time entries but does not reverse the phase's billed-to-date, so a deleted-and-reissued invoice can propose nothing at all.

In use

Jul 6–10

QuickBooks invoice push

BQE webapp endpoints · QuickBooks

Pushes a batch of finalized invoices from BQE into QuickBooks, captured the same way as the send endpoint. Runs as the last step of the monthly chain, after invoices are sent and archived, so "send out invoices" means the whole sequence rather than a per-step prompt.

In use

Aug 10

Invoice PDF archiving to SharePoint

Graph · SharePoint

Sent invoice PDFs are uploaded into each project's invoices folder using the firm's filename convention, so the archive matches what the client received without anyone filing it by hand.

Updates

  • Sep 8 When a retainer was applied to an invoice, the archived filename now carries -R directly after the phase code — CA-R thru Jul, CD100-R. BQE's public API exposes no retainer field on an invoice, so detection reads BQE's retainer records instead. Existing archived files are not retro-renamed, apart from one corrected at the time.
Completes the monthly chain: worksheet → invoice → send → archive → QuickBooks — roughly 1,200–1,300 manual clicks per PM reduced to about 10 interactions, across 5 PMs

In useJuly 2026 was the first month run fully through the chain.

Jul 23

Power Automate flows as code

Power Automate API

Proved that flows can be read and deployed programmatically through the Flow API rather than clicked together in the designer, which required a one-time Entra permission change. Every existing flow is dumped to JSON, and deployment is a scripted set of documented ID swaps. Flows are always deployed in a Stopped state for human review before being turned on. This is the capability every later pipeline rollout depends on.

In use

Jul 23 →
Aug 28

2027 year setup

SharePoint · Power Automate · PnP

The 2027 project year was stood up as a scripted clone of 2026 rather than rebuilt by hand: lists copied faithfully down to internal column names so flow clones need no edits, then each flow deployed Stopped pending review. Cloning surfaced a latent bug in the live 2026 pipeline-refresh flow — project count crashed when a status had zero rows — which was patched in both years.

Updates

  • Aug 28 Audit found two deviations from the firm pattern on the new site: it was the only year site not associated with the projects hub, so it had no hub navigation, search scope or theme; and Members was set to "Everyone except external users" — SharePoint's creation default — meaning every internal account in the tenant could edit 2027 project files. Both corrected and scripted for future January cutovers.

Updates

  • Sep 2 The 2027 pipeline refresh was turned off. It is a daily recurrence flow, so it ran and failed every morning against empty 2027 lists. The setup flows are trigger-based on that empty list, so they never fire and never fail — those stay running.
  • Sep 10 The 2027 Project Setup flow was stopped as well. The 2027 Potential Project Setup flow remains Started.

In use2027 pipeline refresh and 2027 Project Setup stopped until the year is active; 2027 Potential Project Setup running.

Jul 28 →
Aug 19

Teams project workspace standardization

Teams · Planner · OneNote · Graph

Tooling to build the Teams channel tabs the setup flow cannot create on its own — a Planner tab bound to the project's plan and a OneNote Notes tab — with configurations copied verbatim from a hand-built reference project so automated projects are indistinguishable from manual ones. Includes a read-only audit reporting which channels are missing which tabs, and HOWTO docs written for a non-technical operator.

Updates

  • Aug 19 Channel tab order standardized across every 2026 project using a create-then-delete sequence, since Teams exposes tab order as read-only.

In use

Jul 28 →
Aug 19

Calc report library

SharePoint · Graph · docx parsing

Engineers repeatedly needed a past calculation report that used a given material — "show me art projects that used bronze" — and there was no way to search for it. A crawler walks from the master Project Repository to each project-year site, into the calculations folder, and picks the newest report per revision family. Materials are read from the document's own heading structure: the report template's unused material subsections are deleted by engineers, so the surviving headings under "Materials" are the materials actually used.

Materials map to a fixed word bank; unknown headings are logged rather than silently written. Projects with no report stay visible as rows with an explicit scan status instead of disappearing. The master list is referenced by GUID so a rename cannot break it, and the crawler is idempotent — the whole list can be rebuilt from scratch on any site.

Updates

  • Jul 29 Full backfill completed across all Art and Staircase projects.
  • Aug 19 Heading parser adjusted for a template variant.
335 rows · 295 art and staircase projects covered · 151 reports found and tagged

Awaiting PM inputUsable now. Standardized tagging rules — word bank additions and the alias table — to be codified pending PM input.

Jul 30

Instagram export workflow

ffmpeg · image pipeline

A repeatable procedure for producing IG-ready assets from the Project Image Library. Web-renamed files are compressed 1800px versions, so each is traced back to its high-res source — matched by aspect ratio and verified with ffmpeg SSIM at about 0.997 — and re-exported from there. Verticals go to 4:5 (1080×1350), horizontals to 5:4 (1350×1080). Photos are center-cropped; technical illustrations are padded with white instead, so a drawing is never cut. GIFs convert to H.264 MP4 at 30fps, looped about three times to clear Instagram's three-second minimum.

In useRecorded as a repeatable procedure in project memory.

Jul 22–29

M365 automation skill file and workspace documentation

documentation

Platform findings consolidated into a reusable ~38KB skill file: tenant structure, verified column internal names, Power Automate patterns, known Microsoft bugs and their workarounds. The root README carries per-project bootstrap prompts naming which repos to clone, which directory to work in, and which context files to read in what order, so a new session starts correctly without being re-briefed. The workspace was also renamed, with scheduled tasks and hardcoded paths repointed the same day.

In use

August 2026

Aug 10 →
Aug 28

Lightroom catalog performance and reorganization

Lightroom Classic

Years of severe launch and browse lag traced to OneDrive Files On-Demand sitting in the catalog's SQLite and preview I/O path, followed by a reorganization of which images live in which catalog.

Updates

  • Aug 10 Catalogs moved out of OneDrive to local storage — roughly 1.4GB of catalog and 24.6GB of previews for the main one. Preferences repointed with backups saved first, and backup-on-exit redirected to a OneDrive folder so the safety net survives without the lag. Standing rule recorded: photo libraries stay on SharePoint, catalogs and previews never sit in a synced folder.
  • Aug 28 Catalogs reorganized by subject. A staff headshots catalog was merged in, and the office image library photos were separated out of the projects catalog into a catalog of their own. End state: three separate catalogs — Project Image Library, Office Image Library, and Portraits.

CompleteThree catalogs, all local, all separate; backup-on-exit targets a OneDrive folder.

Aug 13–17

Invoice email auto-filing

Graph · shared mailboxes

BQE's invoice and past-due notifications pile up in the accounting inbox and have to be filed into the right project folder in the right project-year mailbox. This does it: the project number is parsed from the message body (not the subject, which carries invoice numbers), the year maps to the corresponding project-year shared mailbox, and a faithful copy is imported into the existing project folder with original timestamps and read state preserved, verified, and only then is the inbox original archived.

The safety contract is stated in the file header: never creates folders, never guesses, never deletes. Unresolvable messages are skipped and logged. A companion script lists which active projects are missing a folder in their year mailbox so a person can create them.

21 invoices filed in the first live run on the accounting inbox

In progressProven on the accounting inbox. Next: test against a PM inbox, then move from this laptop to an event-driven Azure Function with a managed identity.

Jul 16 →
Aug 28

Project mail folder cleanup and naming

Exchange · Outlook COM · Graph

The shared project mailboxes had accumulated pursuit mail folders that no longer correspond to anything, plus folders left behind when a pursuit converted to a won project. Each mailbox's folder tree is cached offline first — folder names and item counts only, never message content — so the analysis runs without a live session.

Two questions are answered per year on exact identifiers only: which pursuit folders have no matching server folder, and which match a real project folder in the same mailbox, meaning the pursuit converted and the folders should merge. Fuzzy matching was tested and rejected after it paired two different addresses that shared only a street name. Item counts are read from the archive as well as the primary mailbox, because these mailboxes keep folder structure in the primary while the mail itself sits archived — a primary-only count reports every folder as empty. Every script is rename-only; nothing is moved, copied or deleted.

Updates

  • Jul 16 Won projects have their mail folder renamed to the final project name and moved from the Potential tree into the Projects tree.
  • Aug 28 Naming convention enforced as <project> - <client>. Seven folders for one client had it reversed, which broke every downstream name parser — everything after the dash reads as the client, so all seven collapsed to the client's name. Corrected.
  • Aug 28 Rename script rewritten for reliability after a recursive archive walk hung twice at thousands of COM round trips; the direct version steps down known paths in about 25 calls.
  • Aug 31 Merges executed for 2012–2017, against the archive store by direct navigation to known paths rather than a recursive walk. Items are snapshotted before moving, because the collection is live and moving while enumerating skips every second message. Two cases were held back rather than guessed: one pursuit excluded because it was not confirmed to be the same project, and one where the worklist proposed two mutually exclusive destinations.
  • Aug 31 Round two run on the rulings for the held cases, including one project that had no mail folder at all — created to match the repository name so the mail landed on the right job rather than on the different project at the same street address.
  • Aug 31 Merged-out folders deleted from both the archive and primary trees, since the two are independent and deleting one leaves the other drifting. Each folder is re-checked for zero items immediately before deletion in the store being deleted from; any folder still holding mail, or holding subfolders, is refused. Deletions land in Deleted Items and stay recoverable.
  • Aug 31 Live audit of every potential mail folder still holding mail, across all years, to cross-check against pursuits marked Won — a Won pursuit that still has its own mail folder is unfinished cleanup.

In progress2012–2017 merged and the emptied folders removed. Remaining years working through the audit.

Aug 17–18

InDesign batch relink after the library migration

SharePoint · InDesign ExtendScript

The marketing library was renamed and renumbered (the project image folder was renamed and renumbered inside a new document library), which broke the stored absolute image path in every InDesign document at once — the firm book, brochures, EOY presentations, per-project sheets going back to 2010. A read-only script snapshotted the sync migration before and after and answered whether the leftover folder was safe to delete; it never deleted anything itself.

The relink script derives each new path by rewrite rule from the stale one, confirms a file exists there, and relinks only on confirmation. Filename search is deliberately not used: the library stores multiple resolutions under identical filenames, so name matching could substitute a low-res copy — a wrong image that looks fine is worse than a visibly broken link. A verification re-run one minute later reported zero newly relinked, which is the pass condition.

1,628 links audited · 103 documents opened · 967 links relinked across 88 documents and 294 images · 9.1 minutes · 640 no-rule and 21 not-on-disk remaining, categorized

Complete661 links left broken by decision — they point at the retired Y: server, whose content was reorganized rather than moved (32 resolvable of 593 measured).

Aug 18

License table: Excel to InDesign registration page

Excel · Python · InDesign

The principal's PE/RA licenses live in Excel but also have to appear in the InDesign qualification documents, so every new license meant updating several places by hand. The pipeline is two stages because ExtendScript cannot read .xlsx: Python flattens the workbook to CSV, then an InDesign script replaces only the text inside the two license lists — creating, moving, resizing, restyling and deleting nothing.

Real domain judgment is encoded as rules: leading zeros are kept because they are part of the number as issued and stripping them can break a state board's lookup; State 1, State 2 and State 3 say "Structural Engineer" in the sheet but issue no separate SE license, so they stay in the PE list. Dry-run and work-on-copy both default on, and the review copy is written outside the synced Templates folder so it never syncs to the whole team.

In useExcel is the single source; the same CSV can feed other destinations via Power Automate.

Aug 17–26

Billing task flows

Planner · Power Automate · Outlook

A closed loop for the monthly billing cycle in Planner. One flow runs every Wednesday at 7am but fires only in the week before the first Wednesday of the month, creating one billing task per PM with the details filled in, so the cycle starts itself rather than depending on someone remembering. A second flow triggers on task completion, filters to billing tasks, and emails the operator — so she learns a PM is done without chasing anyone.

Updates

  • Aug 26 Start-time condition corrected.

In use

Jul 21 →
Aug 19

Pursuit pipeline dashboards

SharePoint · Power Automate

Each project year has a six-row dashboard on its site home page — one row per pursuit status, with a count and a contract-fee sum — refreshed every morning by a flow that aggregates the year's pursuit list. Clicking a status drills through to that status filtered in the full list.

Updates

  • Aug 18 Won status added to both 2026 and 2027, which had been missing from the dashboard entirely.
  • Aug 19 Drill-down links pointed at the All Items view rather than the filtered Potential view, so Won drills through correctly. Won project names now link to the project's actual document folder.
  • Sep 10 The daily refresh flow was still summing the old field name after the Base Fee rename. Patching it through the API was rejected, because the connector caches its schema by the field it was built against. A manual refresh script computes the six status totals directly against Base Fee as an immediate substitute; the flow itself needs the field re-selected in the designer.

In progressThe scheduled 2026 refresh flow is still running unmodified since Aug 25. Until Base Fee is re-selected in its designer, its totals are not reliable; the manual refresh script is the correct figure.

Aug 10–25

Proposal sweep

SharePoint · Graph · docx parsing

A daily 6:00am job that keeps the live pursuit list honest with no human touch: it detects the newest firm-template proposal in a pursuit folder, advances the status from In Discussion to Proposal Sent, extracts the contract fee from the proposal document, and files superseded drafts into a superseded-contracts subfolder. Ambiguous cases are flagged rather than guessed at.

Updates

  • Sep 10 Defect: after the fee column was renamed Base Fee on Aug 31, the sweep kept writing every fee it computed to the deprecated Contract Fee field. Fixed to write Base Fee, and every value written in between copied across. In the pipeline kit the fee field name is now configurable rather than hardcoded, so a rename cannot silently strand it again.
First live run: 12 statuses corrected, 13 fees written, 19 superseded drafts filed

In useRuns daily, before the pipeline refresh.

Aug 19–28

Project pipeline kit

Power Automate · SharePoint · PnP

The tenant-agnostic parts of the firm's pipeline automation extracted into a separate private repo that can be deployed into any Microsoft 365 tenant. It ships three flows — pursuit folder setup, daily pipeline refresh, and full project setup on Won (Teams channel, standard folder structure, Planner plan with standard labels and buckets, proposal files moved into Contracts) — plus a surface-configuration script for list columns, views and formatters.

The README is a deployment runbook rather than a code dump: no stored secrets anywhere, a stated modularity contract with each module's "what it needs to be worth enabling," a client questionnaire as step zero, a licensing cost table, a ten-check verification list, and failure modes documented from experience — including that writing to the trigger row mid-run re-fires the trigger and stamps a successful setup as Failure, and that a naive historical backfill sets Status = Won and thereby creates duplicate channels, folders and plans for every past project.

Updates

  • Aug 25 Auto-numbering, runtime name resolution and the re-trigger rule carried across from the firm's versions.
  • Aug 26 Views sort and lead with the pursuit number rather than Title, so the newest pursuit lands on top.
  • Aug 28 Unique pursuit number enforcement added.

In useKept in sync as fixes land in the firm's versions: patch the firm's version, port config-driven, document the reason, push both.

Aug 21 →
ongoing

Historical pursuits database, 2009–2023

file server · docx parsing · SharePoint

A searchable record of every pursuit the firm has chased back to 2009, including what each lost pursuit was worth — information that previously existed only as folders on a file server. The server is reachable only over a full-tunnel VPN that cuts the automation's own connectivity, so folder trees were cached to disk year by year (names, dates and sizes only) and every analysis run offline afterward.

Fee extraction handles three proposal conventions by era — a prose sentence for 2011–21, per-phase itemization with no total in 2022, a fee table from 2023 — and anchors on the word "fee" rather than loose currency, because these documents also contain hourly rate cards, retainers, insurance limits and construction budgets that would all look plausible as a total. Implausible values are flagged for human review rather than entering a total, and 2009–10 files in the older binary Word format are read through Word COM, recovering folders that had wrongly reported no proposal.

Pursuit-to-project matching is where the discipline shows. A first similarity-scoring pass produced 441 matches with 262 collisions — one project claimed by up to eight pursuits, because words like "West" and "Street" are shared — and was thrown out entirely. The replacement links only on evidence: a shared street number, identical distinctive names inside the right year window, or corroborating file-inventory overlap between the two folders, tiered by strength, one-to-one enforced, contested projects dropped rather than arbitrated.

Updates

  • Aug 21 2024 and 2025 pursuit lists created and backfilled, mirroring the 2026 structure column for column — 507 pursuits.
  • Aug 27 Raw fee tables cached per year so parsing questions can be re-answered offline; three parser bugs had each cost a full VPN round trip before this existed.
  • Aug 28 List published by cloning the 2026 schema so internal names match and anything reading a year list reads this one. Scope fixed at 2009–2023, since 2024–26 have their own lists and duplicating them would create two sources of truth.
  • Aug 28 Parser shorthand in the notes column rewritten into plain sentences for readers, touching only machine phrases and leaving hand-written notes alone.
  • Aug 31 Completeness measured properly: a column-by-column fill rate read from the live list itself rather than from notes about it, so the gaps are counted rather than assumed.
  • Aug 31 2023 zero-fee gap worked from files. Where BQE shows no contract amount, the project's master file folder is checked for a proposal or invoices, so the figure comes from a document rather than being left blank.
  • Aug 31 Runbook written for the historical won-data work, and a data typo corrected.
  • Aug 31 Won fees now extracted too, 2009–2022. The fee parser had only ever been pointed at pursuits that were lost; it now reads won projects' proposals as well, so the record carries what the firm charged as well as what it bid and lost. Rows written per year, then merged.
  • Aug 31 Under-summed fee audit. Every row holding a single clean fee is re-checked against its source document: if the document names more than one phase, each near its own dollar figure, the extracted number may be one phase's subtotal rather than the true total. Those rows are flagged for a person to read — never auto-corrected.
  • Aug 31 Year values corrected several ways — against the proposal date, sequentially, and against the live list — plus a pagination bug that had been truncating reads, an On Hold versus re-pursued distinction, and a full notes reconcile.
  • Aug 31 ASCII sanitization applied to every write. Standing rule recorded: never write curly quotes, dashes or accented characters into SharePoint from any source. A source document having a curly apostrophe is not a reason to carry it downstream.
18 years cached · blank means "no fixed fee known" — writing 0 would assert the work was free · composed rows reviewed as CSV before any SharePoint write

In progressList built and populated. Ongoing: spot-check review against source, fee values flagged for review, and pursuit-to-project links where evidence was insufficient to match.

Aug 25–28

Pursuit list standardization and numbering

SharePoint · Power Automate · Graph

Pursuit lists across 2024–2027 forced to one canonical 12-column layout — same display names, same grid order, same form order. This also fixed a long-standing complaint that renames never stuck: SharePoint's UI and PnP renames silently no-op, and only a Graph PATCH on the column persists.

Pursuit numbers are now issued by the system rather than typed, rolling over at month boundaries, with flow concurrency pinned to 1 so two simultaneous entries cannot collect the same number. Uniqueness is enforced at the SharePoint level after a defect where a number was freed at project setup and handed to the next pursuit, making it unusable as a join key. A Notes column was added for relationships the schema cannot express — a job bid through two architects, a number reassigned when one pursuit became two projects — with the convention that each note names the related pursuit number so a text search finds both sides.

Updates

  • Aug 26 Vocabulary standardized across all years: "Potential Projects List" became "Project Pursuits," "Potential Project Pipeline" became "Pursuit Pipeline."
  • Aug 28 "Potential No" made the first column on every pursuit view in every year, including the Won view — previously the one place the pursuit-to-project link matters most was the one place it did not appear.
  • Sep 2 Project Number repaired on three projects, which were written in the combined number-and-name form the day before the columns were split and kept it. The Won flow preserves whatever is already in the field, so it will not reintroduce this.
  • Sep 10 The deprecated Contract Fee and Project Type fields hidden from every form. Removing a field from a view only affects the table; the item panel shows every field unless the field itself is marked hidden. Data untouched.
  • Sep 10 Loss Reason added to 2026 pursuits as a multi-select, so a row can carry more than one reason: Budget, Lost to Competitor, Timing, Scope Change, No Decision, Client Delayed, Other. Each renders as a colored pill.
  • Sep 10 Placing that column wrote the view's raw XML and collapsed the default view to a bare list. Restored through the view's typed properties. Rule since: never set a view's XML wholesale — SharePoint honors only some of it.

In use

Aug 24 →
Sep 1

2026 staff rate rollout

BQE API · Task Scheduler

A firm-wide bill and cost rate increase for all employees, effective 1 September, driven off a config file transcribed from the Grade Billing/Cost Rate Summary. Because everyone is salaried it also zeroes overtime rates and turns off auto-overtime. The script defaults to a dry run printing every before-and-after change; the dry run was executed and logged, and a scheduled task will apply it at 00:01 on the effective date. It runs on the public API, so it needs no browser session.

Updates

  • Sep 1 Executed on schedule at 00:01. Log records the before and after rate for every person, and overtime zeroed where it had been set. Applied to all employees with none skipped.
All employees applied 2026-09-01 · 0 skipped · bill and cost rates plus overtime settings, each change logged

Complete

Aug 26

Budget rate repair

BQE API

Budget lines on a new project showed zero and could not be edited without deleting and recreating them. The cause: BQE's web UI auto-fills a budget line's bill and cost rate from the assigned person, but the public API does not, so every line the automation had ever written stored null rates. Fixed forward in the project setup script and backward across existing projects, with a clean re-scan afterward. Because rates are now read live from employee records, the September rate change flows through without touching any script. The repair also handles BQE's 100-request-per-minute cap by backing off and retrying rather than dying mid-run.

51 budget lines repaired across existing projects

Complete

Aug 26–27

Marketing activity code cleanup

BQE webapp endpoints

Standardized time-entry activity codes on the firm's internal marketing project so every marketing phase and sub-phase offers the same list. The Marketing group was applied across all 41 nodes and a stray "Billable Activity Group" removed, since the project is non-billable. Separately, deleting the obsolete "Technical Images" group had been failing with a SQL error because it was still attached to 38 phases hidden in the inactive bucket of Project Assignments; a script strips all 38 assignments so the group can finally be deleted, leaving phase names untouched. Both scripts are dry-run by default and idempotent, re-reading live state each run, and backups of prior code assignments were captured first.

41 nodes standardized · 38 stale assignments removed · backups taken before both changes

Complete

Aug 26

Client contact email maintenance

BQE webapp endpoints

BQE's public API has no contact-update endpoint, so the webapp's own update call was captured and turned into a script that edits an existing contact's email list in place — reusing the known-good create payload with the record ID and a Modified state swapped in — instead of creating duplicate contacts. Standing default recorded alongside it: every project for one architect gets a per-project contact whose email list always includes two standing billing addresses plus whatever Invoice C/O the contract names.

In use

Aug 20–27

Ops and Marketing OneNote notebooks

OneNote · Graph

Two governed notebooks built and maintained through the Graph API rather than typed: 2026 Marketing (MKT-01–06) and 2026 Ops (OPS-01–09), fifteen numbered function pages plus a 00 Overview page in each. Every page states what the function is for, then separates what is built from what is in testing from what is still open, and names known gaps as content rather than omitting them. The overview pages establish one ownership statement, an ownership map, systems of record (one governing system per data type), technology stack, operating cadence, and dependencies.

Maintenance scripts PATCH pages in place, so hand-edited text and manually set column widths survive automation; house-style passes for punctuation, title case, table cleanup and project sort order run against the live pages. Graph has no page-reorder endpoint, so ordering is achieved by deleting and recreating pages in sequence — documented rather than worked around silently.

Updates

  • Aug 21 First pages pushed from the strategy outlines, which carried an explicit working protocol: iterate section by section, ask rather than invent.
  • Aug 25 Full nine-page Ops rebuild.
  • Aug 26 Overview pages created and an ownership block prepended to every page without disturbing existing content; the Teams billing thread mirrored into the Billing Operations page under a note explaining the source.

In use

Aug 25–27

Visio workflow diagrams and Planner task export

Visio COM · OneNote · Planner

Workflow diagrams are authored by script rather than by hand, via headless Visio automation, with the drawing standard codified in the builder: every shape sits fully inside exactly one lane, cross-lane work is expressed by connectors moving between lanes rather than shapes floating on a boundary, review back-and-forth is drawn as a real loop, and the brand palette and type sizes are fixed. Diagrams are embedded into the OneNote page as an image plus a link to the editable file, since OneNote's API cannot host a live Visio object.

A read-only export then reads the same file as the source of truth: each process shape becomes a task, the lane band its position falls in determines the owner, left-to-right order determines sequence, decision diamonds become checkpoints rather than tasks, and connector data yields real predecessor and successor links including backward loops. Output is a Planner-shaped CSV. Diagram and task list cannot drift apart, because both derive from one file.

A reference architecture document defines the four layers this runs through — source definition, OneNote display, SharePoint data, Planner execution — and inventories nine candidate workflows, each labeled by maturity rather than presented as ready.

2 workflows drawn (video approval, website post) · 15-step task CSV generated from the video approval diagram

Updates

  • Aug 31 A second Visio file added for operations, opening with a New Hire Onboarding page drawn to the same conventions as the marketing workflows — strict swimlanes, uniform boxes, brand palette, one shape per Planner task.
  • Sep 10 Video Approval page rebuilt with "Request caption from PM" removed, because the Planner gate replaces it: the caption task appearing on the PM's board is the request. Everything else kept, including the revise loop and the parallel color-grade and export branches.

In useThree workflows drawn across two files. The Planner execution layer that consumes them was built Aug 31 — see the task release entries.

Aug 29–31

Website Post task release

Planner · Power Automate · SharePoint

The execution layer underneath the Visio workflows, and the answer to a real problem with putting a multi-step process into Planner: create every task up front and everyone's board fills with work they cannot start yet; create them one at a time and nobody can see what is coming.

Every task in a workflow instance is created up front and parked in a Blocked bucket, unassigned, so it reaches nobody's board. When a task is completed, the flow looks it up in a Workflow Task Sequence list, finds the next step in the same workflow and project, assigns it to its owner and moves it into that project's released column. Assignment is the release mechanism, because Planner's My Tasks only surfaces tasks that carry your name. The sequence lives in a SharePoint list rather than inside the flow, so changing a workflow is a data edit.

Updates

  • Aug 29 Website post workflow drawn as page 2 of the Visio file, to the same swimlane conventions as the video approval diagram, and the website page updated.
  • Aug 31 Release flow deployed Stopped, for review before it is started.
  • Sep 14 Confirmed running: flow state Started against the live tenant.

In useRelease flow Started.

Aug 29

Miro board as a Teams channel tab

Teams · Miro API · Graph

Pins a project's Miro board as a tab on its Teams channel, matching the configuration the Miro Teams app produces except for the board-specific access token, which Miro's public API does not expose. The embed relies on the viewer's own Miro sign-in instead, which is the open question the build exists to answer. Boards are matched by project number with exactly one match required — the script refuses rather than guessing — and it skips any channel that already has a Miro tab.

Updates

  • Sep 2 Boards were private to their creator. The tab builder runs on one account's token, and a Miro board created with nothing but a name belongs to whoever created it — so a PM opening the Miro tab in their own project channel hit an access-request screen. Reported by PMs Aug 31. The creation path is fixed, and a separate script opens the boards already out there to the team.
  • Sep 2 That repair is deliberately narrow: it touches only boards named exactly like a Teams channel, created after the tab builder's first commit, by the automation's own account. Boards people made by hand are left alone even when they are project-numbered and private, because someone may have meant that and it is not the script's call.

In progressAccess repaired; whether the tokenless embed renders for a signed-in viewer is still what is being tested.

Sep 1–2

Project web-page mockups in Miro

Miro · Figma REST

Website page mockups moved off Figma and onto the Miro project boards, so the mockup sits with the rest of a project's marketing material rather than in a separate tool. A minimal Figma REST client was written first to read the existing files — the REST API works on any plan, whereas the Dev Mode integration is rate-limited on the Starter plan.

The builder generates a page in the firm website's style as a frame on the project's board, with the grid, type scale and block grammar measured off the live site rather than eyeballed: a 1728 container on twelve columns, alternating image-left and image-right rows, paired images sitting side by side rather than stacked, and a fixed type scale. Copy is deliberately left as lorem, because a second script pulls the real paragraphs out of a Miro document widget on the same board and drops them into the frame's text boxes — Miro has no native binding between a document and a frame, so that script is the link: edit the doc, re-run it, the mockup catches up. Text slots are matched by role and width rather than by hard-coded id, so the mapping survives a rebuild, and any shortfall or overflow is reported rather than silently truncated.

In progressBuilt and running against real project boards.

Aug 31

New Hire Onboarding workflow

Visio · Planner · Power Automate

Onboarding drawn as a swimlane workflow in a new operations Visio file, following the conventions set by the marketing diagrams, with one shape per Planner task. Its release flow is the same gate engine built for the website post workflow: every task is created up front and parked unassigned in a Blocked bucket, and completing one assigns the next step to its owner and moves it into the released column.

Updates

  • Sep 14 Confirmed running: release flow state Started against the live tenant.

In useRelease flow Started.

Aug 31

Grade template audit and PowerGrade publishing

DaVinci Resolve API · Python

Publishing a grade template only works if the template is genuinely clean, and a node that looks empty in the UI can still carry a power window, a qualifier, a leftover OFX or a LUT. The audit mode prints every node of every clip on a timeline — index, label, and the tools actually present — so a template can be confirmed empty by name before it is published, rather than assumed. The build mode then publishes it as a PowerGrade. Audit is read-only.

Updates

  • Sep 3 Defect: the publish step deleted the album's existing stills before grabbing new ones, so a failed grab left the album empty. It now grabs first, checks the count matches the timeline, and stops without deleting anything if it does not.

In useTemplate documentation page published alongside it.

Aug 25 →
Sep 10

Won project setup flow

Power Automate · Teams · Planner · SharePoint

When a pursuit flips to Won, the flow provisions the project: it issues the project number, creates the Teams channel, copies the F-folder structure, moves the proposal files across, deletes the emptied pursuit folder, creates the Planner plan, and stamps the row with a setup status.

Updates

  • Aug 25 The flow now issues the next project number itself, using the Teams channel list as the number registry, and resolves the project name from the newest proposal at run time rather than waiting for the overnight sweep, with a fallback so the name is never empty.
  • Aug 25 Defect: an early write-back to the row re-triggered the flow, and the re-queued run failed on "channel already exists," recording Failure on a setup that had succeeded. Consolidated to a single write at the end of the run.
  • Sep 10 Generic repair for stalled setups. The flow has stalled three times for three different reasons — an Office-locked file on one project, an overnight completion racing a backfill on another, and a file move failing outright on a third. The repair was the same shape each time, so the one-off fix written for the first was generalized: finish the moves, delete the emptied pursuit folder, create the Planner plan, and mark the row Success, in the flow's own order.

In use

Sep 3

Grade sync between timelines

DaVinci Resolve API · Python

Copies clip grades from one timeline to another for the clips that appear on both, so a regraded master does not have to be redone by hand on every cut derived from it. The default run changes nothing: it reports which clips are shared, which source grades exist, which destination clips are already graded, and what it would do. --apply copies the grades; --overwrite is required before it will replace a grade that already exists on the destination.

In use

Sep 10

Carousel slide export from one timeline

DaVinci Resolve API · Python

Renders one file per carousel slide from a single timeline — the equivalent of slicing from guides, for video. Resolve's own individual-clips render cannot do this, because it renders every clip separately, so a slide built from two or three clips comes out as several files. This queues one render job per slide instead.

By default the gaps define the slides: clips that butt together form one slide, and blank frames start the next. Clip markers, where placed, tighten that slide's in or out point. An unmarked timeline therefore splits correctly with no preparation, and marking the one slide that needs a tighter cut does not disturb the others. Alternative modes cover gaps-only and other splits.

In use

Aug 26–28

Outlook follow-up agent

Power Automate · Graph · Outlook

Follows up twice, three days apart, on enrolled emails that go unanswered, written in the sender's own voice with her full signature block embedded in the HTML, since automation-sent mail never picks up the Outlook client signature.

The design decision that matters: the mailbox is the only state. Applying an Outlook category to a sent message enrolls the thread; removing it cancels everything; a reply from the recipient clears it; and the flow's own reply becomes the counter on the next hourly sweep, so nothing needs to be stored. No tracking list, no database, no config UI, and categories are invisible to recipients.

Updates

  • Aug 26 Verified in test mode on a synthetic self-addressed thread, both branches passing. Deployment is mode-switched — test points at the operator with a two-minute threshold, production at the real recipient with three days — as an in-place update of one flow rather than a second flow.
  • Aug 28 Live defect found and fixed the same day: Graph's reply on your own sent message addresses you, not the original recipients, so the first follow-up went to the sender. Recipients are now set explicitly. The self-addressed test could not have caught this, since sender and target were the same address.
  • Aug 31 Incident — hourly runaway, 25 duplicate follow-ups. The flow re-sent the same follow-up on one thread roughly hourly from Aug 28 14:13 to Aug 31 13:13. Root cause: it identified its own messages by matching the sender's SMTP address, but replies it sends store an Exchange legacy DN in that field instead — so it never counted its own sends, and neither the two-send cap nor the three-day clock ever advanced. The second enrolled thread escaped the loop only by accident: its Aug 28 mis-send landed in the sender's own inbox, and that copy did resolve to an SMTP address, so it got counted.
  • Aug 31 Incident response: flow stopped, both enrollments cleared, and the sends counted per thread.
  • Aug 31 Fix: own-message counting now queries Sent Items for the conversation — folder membership rather than sender matching — so the newest in-thread message is unambiguous. A spacing guard was added on top: a send requires the newest in-thread message to be older than the threshold, which makes more than one send per threshold window structurally impossible even if the counting breaks again.
  • Aug 31 Loop-terminator test built against a target that never replies, since the original self-addressed test could not catch this class of bug — the flow's own follow-up matched the replied branch and retired the thread before a third sweep. All six checks passed.
  • Aug 31 Standing rule recorded: never use a project or shared mailbox as a test target without explicit approval of that exact mailbox. The loop test put two test messages into a shared project inbox without asking first.
  • Sep 2 Checked for any send after the last runaway message before restarting; none found.
  • Sep 2 Second follow-up reworded: the first keeps the greeting and thank-you, the second drops both and reads only "Just following up on below," so two identical nudges never arrive.
  • Sep 2 Start script added that refuses to start the flow unless the live definition is in production configuration, so it cannot be switched on while still pointed at the test settings.
  • Sep 2 Restarted in production. Verified against the live flow on Sep 14: state Started, hourly sweep, three-day threshold, recipients set explicitly on every send.

In useRunning in production since Sep 2, after the Aug 31 fix and loop test.

Aug 20 →
ongoing

Resolve media relink and proxy repair

DaVinci Resolve API · PowerShell · Python

The same August library reorganization that broke InDesign also broke stored clip paths in Resolve. Resolve-Doctor consolidates a dozen single-purpose scripts into one tool the operator runs unattended while on the VPN — which is precisely when the assistant cannot be running, so it had to work standalone with documentation good enough to run without help. Scanning is the default and changes nothing; the fix mode repairs only paths it can confirm exist on disk; every run writes a timestamped CSV. Folder-mapping rules live as editable data at the top of the file, so the next rename costs one line.

The guards are the substance of it. An unmounted drive is not a missing file — without that distinction a fix run with the VPN down would have discarded 136 valid proxy pointers. A clip has two paths, and dead proxy pointers are invisible in Resolve because it silently falls back to the original. Renamed files are never matched by guess; confirmed renames go into a map that starts empty on purpose, and anything ambiguous is reported for a person rather than resolved. The filesystem is re-read after every write, because earlier work reported "21 relinked, 0 failed" while saving nothing. Report verdicts use a graded vocabulary so ambiguous cases route to a human, and rows name the bin, the timelines using the clip, and the timecode of first use written so twelve minutes cannot be misread as twelve seconds.

Updates

  • Aug 20 Seed script written, carrying the InDesign relink method across to Resolve with a project-name guard so it refuses to run against the wrong open project.
  • Aug 26–27 Consolidated into Resolve-Doctor with scan and fix modes; a companion watcher added because a modal dialog in Resolve freezes the script mid-API-call, making "working hard" and "stopped waiting for a click" look identical from outside.
  • Aug 27 Measured that the targeted replace operation works where Resolve's own folder-search relink does not — 40 of 41 versus 11 of 52 on one project. Three renamed overlay clips confirmed by eye and added to the rename map.
  • Aug 28 First VPN fix run: 44 confirmed repairs on that project onto real server paths.
  • Sep 3 A second project restructured on disk to the standard video template, adding the Video and Audio levels under Temp Videos. The path rules were extended to match, so existing clip paths still resolve after the move.
  • Sep 11 Further fix run.
733 clips inventoried · 241 dead proxy pointers surfaced · 136 valid pointers protected from deletion · all 536 remaining offline references traced to the unmounted server, nothing genuinely missing

In progressRepairs continue on VPN runs. One project is relinked through the UI rather than by script, after scripted property reads triggered a codec crash.

Aug 27

Resolve bin and timeline standardization

DaVinci Resolve API · Python

A house bin standard applied across all eight active projects: a canonical order of Timelines, Video, Audio, Graphics, SFX, VFX and Overlays, Compound. Resolve displays bins in creation order and cannot rename or reorder a folder, so the only way to impose an order is to move everything to Master, delete the bins, recreate them in sequence and move the clips back — with the bin-to-clip map written to CSV before anything is touched, so a failed run is recoverable by hand.

Legacy names were normalized to the standard (Footage to Video, VFX to VFX and Overlays, Logo & Texts to Graphics/Logo), an Audio bin with VO, Music and SFX children enforced, loose clips swept in from Master, and an unnecessary codec level flattened out of the Video tree. Timelines were verified to start at 00:00:00:00 and paths checked against the 260-character Windows limit. The bin tree is a fixed template, not a mirror of the folder tree: the folder-mirroring script sorts alphabetically and reproduces folder shapes that do not belong in bins, so it is not used to build them. All projects were exported to .drp beforehand, since a .drp captures bins, timelines, paths and grades whether or not media is online.

Updates

  • Sep 8 Video project setup reference written, derived from one project as the correct reference build and cross-checked against another. It records the folder template and the bin template as two different shapes maintained separately; that bins carry no codec level; that five bins have no folder equivalent; and that project-specific bins sit on top of the template. Temp Videos is the working edit store, edited from SharePoint and archived to the server only at end of life, with cross-user path consistency coming from a shared drive mapping over each person's sync folder rather than from moving media to the server.
8 projects standardized · bin map written to CSV before any change · every timeline starts at zero

In useStandard applied across all eight projects and used for new ones; Resolve version upgrade still pending.

Recurring conventions

Working rules applied consistently across the systems above. Each is enforced in code or documented in the relevant rules file.

  1. Dry-run default. Write-capable scripts preview by default and require an explicit flag to act. The past-due mailer adds a middle tier that sends real email to an internal address only.
  2. Independent verification. Success is confirmed by re-reading the target, not by the tool's return value: filesystem re-read after Resolve repairs, a verification re-run after the InDesign relink, Exchange message trace after BQE reports a send, worksheet totals read back after a billing build.
  3. No guessing on ambiguity. Fuzzy matching was evaluated and rejected in both the pursuit-matching and mailbox-cleanup work. Scripts act on exactly one match or decline and log; ambiguous cases go to a person.
  4. Path derivation over filename search. After a folder reorganization the new path is computed by rule and confirmed on disk. Filename search is never used where a library holds duplicate names across resolutions.
  5. Single source per fact. The license spreadsheet drives the InDesign page; one Visio file produces both diagram and task list; the manager's worksheet note drives bill-through capping; the Outlook category is the follow-up agent's only state.
  6. Configuration over code. New projects, years, staff, keywords and folder renames are config entries, not new scripts.
  7. Rules files per system. Failures and platform behaviors are recorded as dated rules — 17 BQE documents, plus gotcha ledgers in the M365 and Resolve documentation — including approaches tried and rejected.
  8. Blank is not zero. An unknown fee is left blank; projects with no report keep a placeholder row; unrepairable links are counted and attributed rather than dropped.
  9. Run logs and backups. A timestamped log per run, state backups before bulk changes, billed months frozen as static ledgers.
  10. Plain ASCII downstream. Curly quotes, typographic dashes and accented characters are stripped before anything is written to SharePoint, from every source. A source document carrying them is not a reason to propagate them.
  11. Operator-runnable and portable. Tools that must run where the assistant cannot are documented for unattended use; the pipeline automation is exported secret-free to a separate kit for deployment outside the firm.