AI and Automation Work

Every project below (49 in total) is an automation or workflow I built and maintain. Original function and subsequent iterations are cataloged.

Start Here

Software
Built with
Status

Tap to filter, tap again to remove. Several picks in one row widen the list; picks in different rows narrow it.

No projects match these filters.

October 2026

Oct 12–14

Staff Expense App

Staff file expenses in a web app, PDF and BQE entry creation automated

Azure Static Web Apps · BQE · Graph · JavaScript · Node · SharePoint

Expense reports were previously entered by hand via PDF template, submitted with receipts, then entered into BQE. Staff now fill information in a custom browser app, attaching a receipt to each line item. Submission returns a completed template with receipts as one PDF, files it in the correct folder, creates BQE expense entries, and emails a notification for reimbursement. An expense can cover several projects at once, split by a designated percentage.

Updates

  • Oct 13 Designed dark and light mode that follows the device setting until it is used. Order of sections rearranged to follow logical flow.
  • Oct 14 Added inline validation: submitting an incomplete report outlines the blank fields in red and auto-scrolls to the first one.

In use

Oct 9–13

Billing Rebuild App

Managers can rebuild billing worksheets on their own

BQE · Excel · JavaScript · Power Automate · SharePoint · SPFx

Each manager rebuilds a worksheet as needed, with no handoff. The quick link tile holds a month dropdown and a rebuild button. The tiles are a custom SharePoint web part, SPFx and TypeScript, deployed through the app catalog in place of a static list of quick links.

Clicking "Rebuild" writes a request row to a list, and a worker builds that month’s worksheet. The tile then shows the state of the run: Queued, Running, Done or Failed with the reason. Both the selector and the worker refuse a month that has already been billed.

Updates

  • Oct 13 The message line on a tile renders at a fixed height, so a tile keeps its size regardless of status messages. A build stopped by BQE rate limiting names that reason in the Failed message.

In use

Oct 10

Billing Notification Card

An adaptive card tells a manager their worksheet is built, with the month's totals

Graph · JavaScript · Power Automate · Teams

When a manager’s worksheet is built, the Workflows bot posts an Adaptive Card in Teams. It carries the month, the row count and the billable total, with a button that opens the workbook at that month’s tab. The script builds the card from the sheet and calls the flow to post it.

In use

September 2026

Sep 21 →
Oct 5

Fillable PDF Creation

Flat forms rebuilt as fillable PDFs

Acrobat · PyMuPDF · Python

Authorization forms that arrive as a flat PDF or a Word file are rebuilt as fillable PDFs so they can be completed on screen instead of printed, written on and scanned back. Every tick box and write-on line in the original document gets a field.

Each form has a signature field, a Clear Form button that asks before wiping entries (and does not print), tab order that follows the page, and embedded fonts so the filled form looks the same in every PDF reader.

Updates

  • Sep 21 A card authorization form for a sponsorship filing was built from the Word original.
  • Oct 5 A notary card authorization was built from a flat PDF.

Complete

Sep 1–2

Web Page Mockups

Project web page mockups built inside project boards

Figma REST · JavaScript · Miro · SharePoint

Website page mockups moved off Figma and onto each project's Miro board, so a mockup sits with the rest of that project's engineering material. The existing Figma files were read through Figma's REST API, used because Figma's Dev Mode integration is rate-limited on the Starter plan.

The script generates a page in the firm's visual style as a frame. Its grid, type scale and block structure are measured off the live site: a 1728-pixel container on 12 columns, rows that alternate image-left and image-right, and paired images in a row.

The frame is generated with lorem ipsum in the text boxes. A second script reads the real paragraphs from a document widget on the same board and writes them in, so editing the document and re-running the script updates the mockup. It matches each text box by its role and width, which survives a rebuild of the frame. Copy that runs short or long is reported for a person to resolve.

In use

Sep 3

Grade Sync between Timelines

Color grades copied from master timeline

DaVinci Resolve API · Python

A DaVinci master timeline is graded once and its derived cuts take the same grades, for the clips that appear on both timelines.

The script reports which clips the two timelines share, which of those carry a grade on the source, and which are already graded on the destination. --apply performs the copy, and --overwrite replaces a grade that already exists on the destination.

In use

Sep 10

Video Carousel Slide Export

One rendered file per carousel slide, cut from a single timeline

DaVinci Resolve API · Python

A custom script renders one file per carousel slide from a single DaVinci timeline. The timeline holds the whole post, and a single slide can be exported with several clips.

Gaps set the slide boundaries: clips that touch are one slide, and a blank frame starts the next.

In use

Sep 10

Video Post Task Workflow

Video post task workflow built in Visio and Planner

JavaScript · Planner · Power Automate · PowerShell · SharePoint · Visio COM

The video post workflow was drawn as a swimlane workflow in Visio, with one shape per Planner task. Includes drafting, reviewing, approval, color grading and captioning. Every task is created up front and parked unassigned in a Blocked bucket, tracked a row apiece in the Workflow Task Sequence List, and completing one assigns the next to its owner and moves it into the released column.

In use

Sep 15–24

Stair Task Workflow

Stair task workflow built in Visio and Planner

BQE · Graph API · JavaScript · Miro · Planner · Power Automate · PowerShell · SharePoint · Visio COM

Each stair project gets the same set of sequential Planner tasks, released one at a time: all are created up front and parked unassigned, and completing one assigns the next to its owner. Includes load calculation, member design, reports and review at each phase. The steps come from a Visio diagram built from the office standards board in Miro, and each project's tasks are tracked a row apiece in the Workflow Task Sequence List. Each project has its own Planner plan and its own release flow.

Updates

  • Sep 15 First live instance seeded on one project, with the Visio page drawn to swimlane conventions.
  • Sep 16 Standardized every stair project with FD buckets.
  • Sep 16 A finished project's release flow is left on, so a weekly job switches it off once every stair task is complete or BQE shows the project Completed, Inactive or Canceled. It touches only per-project stair flows, and a failed read leaves them all alone.
  • Sep 24 Design Assist Staircase Task Planner variant added, drawn as its own Visio page from a Miro frame. The script picks the variant from the plan's buckets, and the duplicate guard and read-back check the variant.
  • Sep 24 Stair project-type detection moved to BQE. The repository row arrived late, so the project type written into BQE during setup decides instead; it exists by the time the workflow is seeded.

In use

Sep 22–25

Lunch Coordination Adaptive Cards

Lunch headcounts and orders collected automatically

Dataverse · Graph API · JavaScript · Power Automate · PowerShell · SharePoint · Teams

The Workflows bot sends each invited person an Adaptive Card in Teams carrying their usual order, which they change or decline on the card. Office and employment-status fields were added to the Dataverse employee records, so an accurate invitation list can be determined. SharePoint Lists hold each person's usuals per restaurant and the event itself.

Updates

  • Sep 22 Employee records extended with office and employment-status fields, which the invitation list is derived from.
  • Sep 23 Invite-and-collect flow built through the Power Automate API.
  • Sep 25 A person can change their RSVP from the invitation, and one follow-up goes out on the deadline day.

In use

Jul 30 →
Oct 16

Instagram Post Queue

A SharePoint queue posts to Instagram automatically

ffmpeg · Graph API · Instagram API · JavaScript · OneNote · Python · SharePoint · Teams

A SharePoint queue of planned Instagram posts publishes itself. Each row names a project, a post label and a post type, and points at the image folder. A scheduled run picks up every approved row that is due, uploads the images or video, publishes, and writes the returned post id back to the row.

The captions and the grouping of images into posts are generated by script from the project's web page, and land on the project's OneNote caption page under a heading per post. The publisher reads the caption from there at post time, and a companion script pushes a row of image thumbnails under each heading so a reviewer sees which images belong to which caption.

Images come from the image library, where each web-resolution file is traced back to its high-res source by aspect ratio, verified with ffmpeg SSIM at about 0.997, and re-exported. Verticals go to 4:5 (1440×1800) and horizontals to 5:4 (1800×1440). Photos are center-cropped; technical illustrations are padded with white 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.

Video carousels are assembled through the API: each clip is uploaded as a carousel item, then the set is published as one post. The 4K originals are transcoded with ffmpeg to the feed's spec at near-source quality before upload, and reels post the same way as a single item.

A review page shows every queued post with its images in posting order and its caption before anything publishes, and a Teams card announces each row once it is posted to marketing team members. Unposted rows are re-dated from their slot on each pass. Videos are re-encoded to Instagram's requirements before uploading, and a dry-run mode builds the upload and stops short of publishing.

Updates

  • Sep 28 Carousel preparation and a short-clip test added, with video re-encoding before upload.
  • Sep 29 Queue view and column formatting built so the queue reads as a schedule.
  • Oct 6 A second scheduled task was added for one-off runs alongside the regular cadence.
  • Oct 15 The caption generator was built. A project’s social images are grouped into posts and a caption written per post from the project’s web page, appended to OneNote.
  • Oct 16 Every photo in the queue is compared with the project’s web page, and the review page outlines the ones still unpublished. The two are different crops, so the comparison aligns the pixels and scores them.

In use

August 2026

Aug 10–28

Lightroom Catalog Reorganization

Lightroom Catalogs Split and Merged

Lightroom Classic

Severe launch and browse lag was traced to OneDrive Files On-Demand sitting in the catalog's SQLite and preview I/O path, leading to a reorganization of which images lived in which catalog.

Updates

  • Aug 10 Catalogs moved out of OneDrive to local storage. 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. Office image photos were separated out of the projects catalog, then merged with headshots.

In progress

Aug 13 →
Sep 16

Invoice Email Auto-Filing

BQE invoice mail filed into the right project folder

BQE · Graph · JavaScript · Outlook · shared mailboxes

A script files BQE's invoice and past-due notifications from the inbox into the right project folder and mailbox. It parses the project number from the message body rather than the subject, which carries invoice numbers. The year maps to the corresponding shared mailbox, and the script recreates the message in the project's existing folder with its original sent and received times and marked read.

The script creates no folders and resolves on exact identifiers only. Once the copy is verified, the original goes to Deleted Items, recoverable from there. Unresolvable messages are skipped and logged. A companion script lists which active projects are missing a folder in their year mailbox.

Updates

  • Sep 15 Extended to the shared project inbox. Invoices for 2022-and-later projects are copied into the project’s folder in its year mailbox and the original deleted; older projects, or ones with no year folder, move into the single matching folder in the inbox’s own tree.
  • Sep 16 Read-only index of direct links to every project mail folder in the year mailboxes, each flagged with active status.

In use

Jul 16 →
Aug 31

Pursuit Mail Folder Cleanup

Dead pursuit mail folders merged into project folders

Exchange · Graph · Outlook COM · PowerShell · Python

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. The analysis then runs without a live session.

Per year, on exact identifiers only, the analysis finds 1) the pursuit folders with no matching server folder, and 2) those matching a real project folder in the same mailbox. A match means the pursuit converted and the two folders should merge. Fuzzy matching was tested and rejected after it paired two different addresses that shared only a street name. Each project mailbox has a primary store and an archive mailbox, and retention moves old mail into the archive while the folders stay in the primary. A count taken only in the primary shows every folder as empty, so item counts are read from both. 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 Potential project 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. One project had no mail folder at all; it was 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.
Show all 7 updatesShow fewer updates
  • 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; 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.

Complete

Aug 17–18

InDesign Batch Relink

967 image links repaired across 88 InDesign documents after a library move

InDesign ExtendScript · JavaScript · PowerShell · SharePoint

The marketing library was renamed and renumbered, which broke the stored absolute image path in every InDesign document at once.

The script rewrites each stale path into its new one by rule, confirms a file is there, and then relinks. Matching is on path alone. Running it again relinked nothing, which is how a clean pass is confirmed.

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

Complete

Aug 18

Licensing Excel to InDesign Page

License numbers flow from the Excel sheet into InDesign

Excel · InDesign · JavaScript · Python

The principal's PE/RA licenses live in Excel but appear in 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.

Leading zeros are kept because they are part of the number as issued, and stripping them can break a state board's lookup. Dry-run and work-on-copy both default on, and the review copy is written outside the synced Templates folder.

In use

Aug 17 →
Sep 21

Billing Task Flows

The monthly billing cycle starts itself in Planner and reports when a manager is done

JavaScript · Outlook · Planner · Power Automate

A closed loop for the monthly billing cycle in Planner. One flow runs every Wednesday at 7am and fires only in the week before the first Wednesday of the month. It creates one billing task per manager with the details filled in. A second flow triggers on task completion, emailing accounting when a manager is done.

Updates

In use

Jul 21 →
Aug 19

Pursuit Pipeline Dashboards

Every project year gets a pursuit dashboard that counts and totals itself each morning

JavaScript · Power Automate · PowerShell · SharePoint

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. A flow aggregates the year's pursuit list and refreshes it. Clicking a status drills through to that status filtered in the full list.

Updates

  • Aug 18 Won status added to both 2026 and 2027.
  • Aug 19 Each status on the dashboard now drills through to the full list, filtered to that status, Won included.

In use

Aug 10 →
Sep 29

Proposal Sweep

A script reads new proposals and advances the pursuit list

docx parsing · Graph · JavaScript · SharePoint

A script runs daily against the live pursuit list. It detects the newest firm-template proposal in a pursuit folder, advances the status from In Discussion to Proposal Sent, extracts the contract fee, and files superseded drafts into a superseded-contracts subfolder. Ambiguous cases are flagged.

Updates

  • Sep 10 Defect: after the fee column was renamed Base Fee, the sweep kept writing to the old one. Fixed, and the values written in between were copied across.
  • Sep 29 The 6:00am run failed to connect in 8 of 71 runs. Reads and updates now retry on connection errors and rate limiting. Creates and deletes retry only on rate limiting, since a dropped connection may already have created the row.

In use

Aug 19–28

Project Pipeline Kit

Tenant-agnostic pipeline automation created

JavaScript · Planner · PnP · Power Automate · PowerShell · SharePoint · Teams

The tenant-agnostic parts of the firm's pipeline automation extracted into a separate repo that can theoretically be deployed into any Microsoft 365 tenant. Includes pursuit folder setup, daily pipeline refresh, and full project setup on Won. The Won setup creates the Teams channel and folder structure, and builds the Planner plan with standard labels and buckets. A surface-configuration script covers list columns, views and formatters.

The README is a deployment runbook, and it records two failure modes from experience. 1) Writing to the trigger row mid-run re-fires the trigger and stamps a successful setup as Failure and 2) a naive historical backfill sets Status to Won, which 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.
  • 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 use

Aug 21 →
ongoing

Historical Pursuits Database, 2009–2023

15 years of pursuits turned into a searchable list with fees

BQE · COM · docx parsing · file server · JavaScript · PowerShell · Python · SharePoint

A searchable record of every pursuit the firm has chased back to 2009, including what each lost pursuit was worth. That information 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. Folder trees were cached to disk per year, names, dates and sizes only, and every analysis ran offline afterward.

Fee extraction handles varying proposal conventions by era: a prose sentence, per-phase itemization with no total, and a fee table. It anchors on the word "fee" rather than loose currency, because these documents contain tables of hourly rates, retainers, insurance limits and construction budgets that would all look plausible as a total. Implausible values are flagged for human review rather than written in as a total. Files from 2009–10 in the older binary Word format are read through Word COM, which recovered folders that had wrongly reported no proposal.

Updates

  • Aug 21 2024 and 2025 pursuit lists created and backfilled, mirroring every column of the 2026 structure.
  • Aug 27 Raw fee tables are cached per year, so a parser bug can be worked out from the cache instead of another trip across the VPN. Three had each cost one before the cache 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.
  • 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.
Show all 10 updatesShow fewer updates
  • 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.
  • 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, so the record carries what the firm charged and 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 three ways: against the proposal date, sequentially, and against the live list. The same pass fixed a pagination bug that had been truncating reads, drew an On Hold versus re-pursued distinction, and reconciled the notes.
  • 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.

In use

Aug 25 →
Sep 10

Pursuit List Standardization

Four years of pursuit lists forced to one schema, with numbers issued by the system

Graph · JavaScript · Power Automate · PowerShell · SharePoint

Pursuit lists across 2024–2027 were forced to one canonical 12-column layout — same display names, same grid order, same form order. It fixed a long-standing complaint that renames never stuck: SharePoint's UI and PnP renames 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, such as a job bid through two architects or a number reassigned when one pursuit became two projects. 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.
  • Sep 2 Project Number repaired where it had been 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 (replaced by Base Fee) and Project Type (replaced by Planner Type) fields were 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.
  • Sep 10 Loss Reason added to 2026 pursuits as a multi-select. Each renders as a colored pill.
  • Sep 10 Adding the Loss Reason column collapsed the default view to a bare list. The view was rebuilt through its typed properties instead. Rule since: never write a SharePoint view's XML wholesale.

In use

Aug 24 →
Sep 1

2026 Staff Rate Rollout

A firm-wide rate change applied

BQE API · JavaScript · Task Scheduler

A firm-wide bill and cost rate increase, driven off a config file transcribed from the rate summary. 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.

Updates

Complete

Aug 26

Budget Rate Repair

BQE budget lines rate repair

BQE API · JavaScript

Budget lines written by script stored no bill or cost rate, so they showed zero and could not be edited without deleting and recreating them. BQE's web UI fills those rates from the assigned person; its public API does not, and the scripts never set them. The setup script now reads each rate from the person's employee record as it writes, and existing projects were repaired the same way.

51 budget lines repaired across existing projects

Complete

Aug 26–27

Marketing Activity Code Cleanup

Marketing time codes standardized

BQE webapp endpoints · JavaScript

Standardized time-entry activity codes on the firm's internal marketing project so every phase and sub-phase offers the same list.

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

Complete

Aug 26 →
Sep 15

Client Contact Email Maintenance

Client contact emails edited through an unpublished BQE endpoint

BQE webapp endpoints · JavaScript

BQE's public API cannot update a contact, so the webapp's own update call was captured and scripted. An existing contact's email list is edited in place, where before a duplicate contact had to be created. A per-project contact always carries two standing billing addresses plus whatever the contract names.

Updates

  • Sep 15 Standing default corrected to a different billing address.

In use

Aug 20–26

OneNote Architecture

Two governed OneNote notebooks built

Graph · JavaScript · OneNote

Two governed notebooks built and maintained through the Graph API rather than typed. Includes an overview page in each. Every page states what the function is for, then separates built vs. testing, and names known gaps as content.

Maintenance scripts PATCH pages in place, so hand-edited text and manually set column widths survive automation, and house-style passes run against the live pages. Graph has no page-reorder endpoint, and ordering is achieved by deleting and recreating pages in sequence.

Updates

  • Aug 21 First pages pushed from the strategy outlines, which carried an explicit working protocol: iterate one section at a time.
  • Aug 26 Overview pages created and an ownership block prepended to every page without disturbing existing content.

In use

Aug 25 →
Sep 10

Workflow/Task Standardization

Workflow diagrams drawn by script in Visio and exported to Planner

OneNote · Planner · PowerShell · Python · Visio COM

Workflow diagrams are authored by script, through headless Visio automation, with the drawing standard codified in the script. Every shape sits fully inside exactly one lane, cross-lane work is expressed by connectors moving between lanes, 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 it sits in determines the owner, and left-to-right order determines sequence. Decision diamonds become checkpoints rather than tasks, and connector data yields 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.

4 workflows drawn: website post, video post, new hire onboarding and stair

Updates

In use

Aug 29–31

Website Post Task Workflow

Website post task workflow built in Visio and Planner

COM · JavaScript · Planner · Power Automate · PowerShell · SharePoint · Visio

The website post workflow was drawn as a swimlane workflow in Visio, with one shape per Planner task. Includes sourcing photos, technical images, layout, copy editing and upload. Every task is created up front and parked unassigned in a Blocked bucket, tracked a row apiece in the Workflow Task Sequence List, and completing one assigns the next to its owner and moves it into the released column.

Updates

In use

Aug 31

New Hire Task Workflow

Onboarding task workflow built in Visio and Planner

COM · JavaScript · Planner · Power Automate · PowerShell · SharePoint · Visio

Onboarding drawn as a swimlane workflow in a new operations Visio file, following firm standards, with one shape per Planner task. Includes hardware setup, directory account creation, groups and licensing, payroll enrollment and software accounts. Every task is created up front and parked unassigned, tracked a row apiece in the Workflow Task Sequence List, and completing one assigns the next to its owner and moves it into the released column.

Updates

In use

Aug 31 →
Sep 3

Grade Template Audit and PowerGrade Publishing

Grade templates audited at every node before they are published as PowerGrades

DaVinci Resolve API · Python

A PowerGrade is a saved color grade that lives at the database level, so publishing one makes it available in every project in the library, including projects created later. A template has to be clean before it goes in, and a node that looks empty in Resolve can still hold a power window, a qualifier, a leftover OFX or a LUT. Audit prints every node of every clip with its index, label and the tools it holds, and changes nothing. Build grabs one still per clip, names them in clip order, exports the .drx and .png, and re-reads the album to confirm.

Updates

  • Sep 3 Publishing used to clear the album before grabbing the new stills, so a failed grab left nothing behind. It grabs first now, checks the count against the timeline, and deletes nothing if they disagree.

In use

Jul 28 →
Oct 8

Project Setup Flow

A pursuit flipping to Won provisions the whole project without anyone setting it up

BQE · Graph · JavaScript · Miro API · OneNote · Planner · Power Automate · SharePoint · Teams

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

The flow cannot create the channel’s tabs, so a separate tool adds them: a Planner tab bound to the project’s plan, a OneNote Notes tab, and the project’s Miro board, matching the configuration the Miro Teams app produces. A read-only audit reports which channels are missing which tabs, and the HOWTO docs are 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.
  • Aug 25 The flow now issues the next project number itself, using the Teams channel list as the number registry. It resolves the project name from the newest proposal at run time rather than waiting for the overnight sweep, with a fallback name if none is found.
  • Aug 25 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. Writes are now consolidated to one at the end of the run, and pursuit-row writes are refused while a run is in flight.
  • Sep 2 A manager opening the Miro tab in their own project channel hit an access-request screen. The creation path is fixed, and a separate script opens the boards already out there to the team.
  • Sep 10 The flow has stalled three times for three different reasons, and the repair was the same shape each time, so it was generalized: finish the moves, delete the emptied pursuit folder, create the Planner plan, and mark the row Success. The same script backfills buckets and labels on a plan that exists but is incomplete.
Show all 8 updatesShow fewer updates
  • Sep 16 Setup continues past the flow into one chain: finish anything the flow left, write the Base Fee, create the BQE project, write its Project Tiers row, and for a stair project seed the stair workflow and deploy its release flow. The flow itself is unchanged.
  • Sep 29 When setup cannot move a file because someone has it open, a watch starts, retries the move every five minutes, and messages the person holding it from the Workflows bot in Teams. The first message is an Adaptive Card with an "I closed it" button; later reminders are plain, so unanswered cards do not pile up. The watcher switches itself on with the first watch and off when the last one clears.
  • Oct 8 Pursuit-row writes are refused while a Won setup run is in flight, so nothing can land on the row mid-run and re-trigger the flow or overwrite what it is writing.

In use

Aug 26 →
Sep 2

Outlook Follow-Up Agent

Unanswered emails followed up twice, with the mailbox itself as the only state

Graph · JavaScript · Outlook · Power Automate

Follows up twice, three days apart, on enrolled emails that go unanswered. The message is written in the sender's own voice, with their full signature block embedded in the HTML, since automation-sent mail does not pick up the Outlook client signature.

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 Deployment is mode-switched on one flow: test points at the operator with a two-minute threshold, production at the real recipient with three days.
  • Aug 28 Live defect found and fixed the same day: Graph's reply on a sent message addresses the sender, 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 The flow sends at most two follow-ups per thread, three days apart. One thread took 25, because it recognized its own sends by SMTP address and Exchange stores a sent reply's sender as a legacy DN, so the count never moved. The self-addressed test could not have caught it, because the flow's own follow-up looked like a reply and retired the thread. Sends are now counted from Sent Items, a send requires the newest message in the thread to be older than the threshold, and the test target is a mailbox that never replies.
  • 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 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.

In use

Aug 20 →
ongoing

Resolve Media Relink and Proxy Repair

Resolve clip paths and proxies repaired

DaVinci Resolve API · PowerShell · Python · SharePoint

The same August library reorganization that broke InDesign image links 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. Automation cannot reach the machine during those sessions, so the tool runs standalone, with documentation good enough to run it 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: the next rename costs one line.

A clip stores two paths, one to the media and one to a proxy, and a dead proxy pointer is invisible in Resolve because it falls back to the original. The media sits on a server reached over VPN, so an unmounted drive is not a missing file: without that distinction a fix run with the VPN down would have discarded valid proxy pointers. Renamed files are matched only from a map of confirmed renames, which starts empty, and anything ambiguous is reported for a person. The filesystem is re-read after every write, because earlier work reported "21 relinked, 0 failed" while saving nothing.

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, plus a second window that reports whether a run is working, waiting on a dialog, or finished. A modal dialog in Resolve freezes the script inside an API call, so from the first window a long job and a stalled one look the same.
  • 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.
  • 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.
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 use

Aug 27 →
Sep 8

Resolve Bin and Timeline Standardization

One bin standard imposed across all video projects

DaVinci Resolve API · Python · SharePoint

A house bin standard applied across all active projects: a canonical order of Timelines, Video, Audio, Graphics, SFX, VFX and Overlays. Resolve displays bins in creation order and cannot rename or reorder a folder. 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. The bin-to-clip map is written to CSV before anything is touched, so a failed run is recoverable by hand.

Legacy names were normalized to the standard: Footage became Video, VFX became VFX and Overlays, and Logo & Texts became Graphics/Logo. An Audio bin with VO, Music and SFX children was enforced, loose clips were swept in from Master, and an unnecessary codec level was 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. The folder template and the bin template are different shapes, maintained separately, and bins carry no codec level. Temp Videos is the working edit store, edited from SharePoint and archived to the server only at end of life. Cross-user path consistency comes from a shared drive mapping over each person's sync folder rather than from moving media to the server.
8 projects standardized

In use

July 2026

Jul 7 →
Oct 5

Project Activity Feed

An engineer's own projects and recent calculation files on their SharePoint page

BQE API · Graph · JavaScript · SharePoint

A SharePoint widget shows an engineer their own active BQE projects, those with more than 5 hours in the last 30 days, alongside recent file activity in each project’s calculations folder. It is personalized by SharePoint’s own [Me] filter, and is being tested with one engineer.

Updates

  • Jul 13 Checkpoint scripts consolidated into a single aggregator, registered as a weekly full refresh on Mondays and a daily folder-activity refresh.
  • Oct 5 A file whose author is not in the name cache writes its row with the person cell blank and reports a warning.

In use

Jul 7–13

Timeline Usage Widget

A floating window that answers whether a clip is used in the open timeline

DaVinci Resolve API · Python · tkinter

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 currently open timeline. A read-only floating window shows, for the current timeline, whether the selected Media Pool clip is used: green for not used, cranberry for used. It lists every unused clip below that. 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.

In use

Jul 9 →
Oct 15

Invoice Creation and Sending

Invoices created and sent from the billing spreadsheets

BQE webapp endpoints · Chrome CDP · JavaScript · QuickBooks · SharePoint

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 22 A send or create run stops if BQE has changed under it.
  • Aug 10 Invoice creation from worksheet y-marks automated, with preview, --stage and --process tiers, closing the last manual gap in the monthly chain.
  • Aug 17 Delivery verification: 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. Graph message-trace checking joined the toolchain.
  • Sep 10 A discount or retainer typed onto a batch row is held only on that screen, so it is verified against the processed invoice.
  • 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.
Show all 11 updatesShow fewer updates
  • 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 being returned.
  • Sep 10 Every sent invoice now copies accounting, per standing rule.
  • Sep 10 Deleting an invoice releases its time entries but leaves the phase's billed-to-date in place, so a reissued invoice comes out empty.
  • Sep 11 A manager marks Return Ret? to apply a project's retainer. BQE sets auto-apply per batch, so projects marked N are staged in their own batch.
  • Sep 21 Monthly billed totals per manager are built from the invoice records to show what was sent out.
  • Oct 15 Billing out a manager’s month runs as one command: send, file email, archive the PDF on SharePoint, push to QuickBooks, freeze that month's billing sheet.

In use

Jul 6 →
Oct 12

QuickBooks Push

Everything BQE sends to QuickBooks goes through one push that counts honestly

BQE webapp endpoints · JavaScript · QuickBooks

Everything BQE sends to QuickBooks runs through one set of scripts: a project’s root and phases when it is set up, a new client when a contract names one BQE does not have, and each batch of finalized invoices. The endpoints are BQE’s own, captured from recorded actions because the public API does not expose them, and the project and invoice pushes share their logic so the two cannot drift apart.

Each push is counted against the ids sent rather than the records returned, and every push can be re-run: it updates existing links instead of duplicating them, adds only what is missing, and touches nothing but QuickBooks.

Updates

  • Jul 6–10 Invoice batch push built, 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.
  • 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.
  • Sep 14 Client push added, so a client created from a contract reaches QuickBooks in the same run.
Show all 8 updatesShow fewer updates
  • Sep 15 An invoice failed to sync because two of its project’s phases had never been pushed. A read-only check now lists every project node BQE shows as not linked to QuickBooks, read from the linked flag on BQE’s own project grid, so gaps are found before an invoice depends on them. Fixes go through the push tools after approval.
  • Sep 30 Rule recorded: deleting a pushed project orphans its QuickBooks customers. They keep the project names, BQE will not adopt them back, and the re-push is blocked until they are dealt with. The order is to inactivate them in QuickBooks first, then delete and rebuild in BQE.
  • Oct 12 A phase will not link while its parent customer is inactive in QuickBooks, and setting the parent active in BQE does not clear it. The parent customer ids now go in the same push batch as the sub-customers, which updates the parent customers and lands them; the parents can go back to inactive straight afterward.

In use

Jul 23

Power Automate Flows as Code

Flows read and deployed as code through the API instead of clicked together

JavaScript · 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.

In use

Jul 23 →
Sep 10

2027 Year Setup

The 2027 project year cloned from 2026 by script, flows deployed stopped for review

Graph API · JavaScript · PnP · Power Automate · PowerShell · SharePoint

The 2027 project year was stood up as a scripted clone of 2026 rather than rebuilt by hand. Lists were copied down to their internal column names, so flow clones need no edits, and each flow was deployed Stopped pending review. Cloning surfaced a latent bug in the live 2026 pipeline-refresh flow: the project count crashed when a status had zero rows. It 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. 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 against empty 2027 lists. The setup flows are trigger-based on that empty list and stay running.
  • Sep 10 The 2027 Project Setup flow was stopped. The 2027 Potential Project Setup flow remains Started.

In use

Jul 28 →
Aug 19

Calc Report Library

Calculation reports crawled and indexed

docx parsing · Graph · JavaScript · PowerShell · SharePoint

Engineers repeatedly reference past calculation reports that used a given material. A crawler walks from the master project database 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 used.

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

Updates

151 reports found and tagged

In use

Jul 22 →
Sep 23

M365 Automation Documentation

Platform findings and bootstrap prompts written down so a session starts correctly

documentation · PowerShell · SharePoint

Platform findings are consolidated into a reusable 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.

Updates

  • Sep 23 SharePoint List build checklist written from real failures: a column added by script is not in any view unless it is put there, so a new list renders as a wall of names; choice values passed untyped create a column with no choices; one view only, since a second stale view is what a reader lands on; and the formatting property that renders a blank cell when misspelled. Each item is a rule with the symptom it produced.

In use

June 2026

Jun 11 →
ongoing

Monthly Billing Spreadsheet

One billing worksheet per manager built from multiple BQE reports, statuses, and dashboards

BQE · Excel · Graph · JavaScript · SharePoint

Each manager's <Month> worksheet is built from BQE open WIP, project status, retainer data, phase information, and client data. Manager marks when to bill, and the marked total drives the invoice batch. The layout, the totals and the edge cases have been revised continuously since June.

Sent invoice PDFs are uploaded into each project's invoices folder using specified filename convention, so the archive matches what was sent without filing it by hand.

Updates

  • Jun 22 The sheet shows Active, Inactive and On Hold phases, and drops Inactive FA/CA phases that have not started.
  • Jun 22 WIP corrected to exclude non-billable time, and a fully-billed fixed phase that still carries open time now flags.
  • Jun 23 Row color encodes state: a ready-to-bill fill, red for the error case, gray fill for On Hold, gray font for Inactive, and banding for active rows. Placeholder fixed-fee contracts handled, since BQE requires a number on a fixed phase.
  • Jun 25 Per-manager builder scripts replaced by one command covering all managers (build, render, verify), designed to be re-run throughout the month as new time entries land.
  • 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.
Show all 32 updatesShow fewer updates
  • Jul 9 v2 layout rolled to the other managers after testing on one.
  • 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 and non-billable time. Active fixed phases now compute as percent complete times contract value.
  • Jul 9 Re-deploying a layout onto a live sheet wiped 12 of a manager's y-marks. Recovered through SharePoint version history and a re-apply script matched on project and phase. Once a sheet is live, only the merge-preserving build touches it.
  • Jul 10 A manager's sheet now includes phases they manage under another manager's root, with those dollars counting toward their total. Notes column keeps each manager's manual note alongside the script's managed tag. Summary tab monthly total wired to month-tab total.
  • Jul 10 Added rule: do not render into a manager'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.
  • 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 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.
  • Aug 14 Added rule: 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 added (v3): A manual Discount column now sits after Non-billable, and the marked total subtracts it.
  • Aug 17 Remaining 2026 month tabs pre-created for every manager, each summary formula matching its tab's layout version.
  • Aug 25 Bill-through-month: when a manager 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 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.
  • Sep 1 Open retainers are refreshed and a per-manager snapshot taken before a rebuild, so manual edits survive it.
  • Sep 8 When a retainer is applied to an invoice, the archived filename now carries -R directly after the phase code. BQE's public API exposes no retainer field on an invoice, so detection reads BQE's retainer records instead.
  • 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, Bill? had been a typed letter since July for that reason. 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 Added rule: 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. Two retainer columns added. Ret Open? is written by the build as Y on a project's final phase row when BQE shows an open retainer balance. Return Ret? is the manager'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? and Notes, have colored headers.
  • Sep 10 Typing Bill thru <Month> in the notes column caps that project's WIP at that 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 The build reports Y-marked billable plus expenses, less discounts, as the amount being billed, with the after-retainers figure below it.
  • Sep 11 The billed-to-date check runs on Active and Completed fixed phases. A phase billed in full is labeled so, flagged red when unbilled time is still booked against it. Invoice drafting skips any Y-marked row reading "Billed in full."
  • Sep 15 A project missing from a manager’s 2026 summary tab is now inserted by script, copying the neighboring row’s live formulas and re-pointing every total.
  • Oct 6 Before-and-after snapshot tool: a manager’s month tab is captured to JSON with its values and key formats, and two snapshots diffed, so a rebuild’s effect on the sheet is visible, including lost formatting. It lives with the scripts rather than in scratch, after a cleared scratch folder kept aborting build chains.

In use

Jun 11 →
Oct 16

Past-Due Invoice Reminders

Batch-sent reminder emails for overdue invoices

BQE · Chrome CDP · JavaScript · Playwright · Power Automate · Teams

Sends one reminder email per project with overdue invoices, routed to the client contact or the client per BQE's own routing. 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

  • 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.
  • 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.
  • Oct 6 Defect: the in-flight payment hold had been dead. The grid parser reads each column from its title attribute, but the ePayment column renders as a badge with no title. Every invoice came back with an empty payment status, and the hold never fired. An invoice with a scheduled ACH payment was about to be chased before it was caught by eye. The parser now reads the badge text, and that invoice lands in the excluded list. Any badge-rendered payment status now holds automatically.
  • Oct 16 A manager is sent their past-due receivable totals as an Adaptive Card from the Workflows bot in Teams, specifying clients with large past-due balances. A button on the card opens their past-due report.

In use

Jun 11 →
Oct 13

Project Setup from Config

A new BQE project built from one config file

BQE · JavaScript · 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, replacing 120–150 manual steps each.

Updates

  • Aug 19 BQE's standing project rules are applied to every node of every project.
  • 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.
  • Sep 14 New-client creation scripted for contracts whose client is not yet in BQE.
  • Sep 15 Project rules backfilled across all active projects after one project’s phases lacked invoicing rules. Phases added outside the setup script never received rules, so the backfill is re-run after any manual phase add. Projects on the billing-status invoice template get specified rules on every node so the status table prints.
Show all 8 updatesShow fewer updates
  • Oct 9 Existing phases are re-priced when a contract arrives after the project was already built.
  • Oct 12 Scope is read from the signed proposal instead of previous drafts or unsigned documents.
  • Oct 13 A phase added to a project after setup now carries its activity groups. The group assignment needs two fields read off the call BQE's own UI makes. Project setup, phase addition and the code assigner now send that shape. A repair script works through existing projects by phase code and reports each node as added or already correct, since BQE has no reliable read for it.

In use

Jun 11–15

Marketing SharePoint Page

High-level marketing dashboard homepage

JavaScript · SharePoint

A SharePoint homepage for leadership showing where each marketing project stands, with cards grouped by discipline, a Kanban board view, and a goals roll-up on its own page. Each entry names an owner and the current blocker, such as photo rights or external coordination. Rows are edited in place. A Website Updates feed on the same page shows projects newly published on the firm's website, each with an image and link to the live page.

In use

Jun 15 →
ongoing

Video Footage Keyword Tagging

Footage tagged against a fixed keyword bank

CLIP · DaVinci Resolve API · Python

Frames are pulled from each clip and scored on the machine itself against a closed per-project keyword bank, so the tool assigns only supplied vocabulary. The result is written as a CSV that Resolve imports into the Keywords field, or applied directly through the scripting API. Because Resolve replaces the Keywords field on import, the tool emits the complete set per clip.

Updates

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

In use

May 2026

Late May →
ongoing

Resourcing Dashboard

Custom staffing visualizations compiling data from multiple sources

BQE API · Dataverse · Excel · Graph API · JavaScript · Outlook · Planner · Power Automate · PowerShell · Python · SharePoint · SPFx

Custom visualizations pull five datasets together to support resourcing decisions, drawn from BQE, the project database, Planner and Outlook. Four of the five are synced into SharePoint Lists on a schedule and the web part reads those rows, since assembling deadlines means resolving each project plan’s own labels before its tasks can be read. Site visits come live from Outlook calendars, which is one query. A SharePoint Framework web part charts how staff are booked, with hours for the current week, projects sorted by staffing role and manager, and a four-week capacity heatmap. A two-week calendar shows upcoming deadlines and site visits.

Updates

  • May 29 A SharePoint Framework web part was deployed as a first test, rendering one staff member’s roles as a donut chart from a SharePoint List.
  • Sep 15–16 A SharePoint List was created, one row per active project with manager, sector, status and staff as people fields. Projects come from BQE, sector and workflow from the project database, and project manager from the pursuit lists. Staffing role assignments are backfilled from a prior spreadsheet.
  • Sep 16 Won setup writes the new project’s row immediately after BQE creation. Staff role resolves initials through a verified staff directory.
  • Sep 17 Project workflow tags synced from the project database.
  • Sep 17 Resourcing Dashboard built as a SharePoint Framework web part, which compiles and type-checks before anything is deployed, and draws real charts. Two more synced lists feed it: actual BQE hours per person, project and week over the trailing 12 weeks, and planned hours from allocations. It shows hours this week against a 40-hour standard and hours over the last 30 days, with projects and logged hours by tier as donuts that drill down to a project table. A whole-team capacity heatmap covers the next four weeks, filterable by manager and employee.
Show all 9 updatesShow fewer updates
  • Sep 18 Staffing Availability section added: free hours per person over the next 2-4 weeks, ranked by fewest assignments. Manager Workload section lists each manager’s active projects, staff, and hours booked and logged. Both BQE syncs now retry on rate limits and dropped connections, after a single blip had stopped the nightly job.
  • Sep 21 The roster list is read straight by the dashboard on page load, so a staff-role edit shows on the next refresh. A fast job every 20 minutes on weekdays refreshes planned hours and the current and previous week of logged time, each in about ten seconds because BQE filters them server-side. The nightly job still does the full rebuild. Shared plumbing now retries BQE rate limits and dropped connections, and holds a lock so the two jobs cannot overlap. It brings each list in line by comparing rows rather than deleting and recreating them.
  • Sep 19–21 Clicking a staff role now lists the people in it, each with their projects, share and hours, and clicking a person switches to their projects in that role. Manager Workload reports logged and booked hours as weekly averages with a full-time equivalent. Lists of names sort A to Z by default, with metric rankings as a choice.
  • Oct 16 Each roster row carries the project’s BQE status, from a scan of every root project.

In use

Filters

Recurring Conventions

  1. Dry-run default. Write-capable scripts preview by default and require an explicit flag to act.
  2. Independent verification. Every write is confirmed by re-reading the target: the Resolve repair was checked against the filesystem, the InDesign relink against a second verification pass, the BQE send against Exchange message trace, and the billing build against the worksheet totals.
  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. Single source per fact. Each fact has one home and everything else is generated from it: 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, and the Outlook category is the follow-up agent’s only state.
  5. Configuration over code. A new project, year, staff member, keyword set or folder rename is a config entry read by a script that already exists.
  6. Rules files per system. Failures and platform behaviors are recorded as dated rules, including approaches tried and rejected.
  7. Blank is not zero. A missing value is written as missing, never as zero: an unknown fee is left blank, a project with no report keeps a placeholder row, and an unrepairable link is counted and attributed.
  8. Run logs and backups. Every run writes a timestamped log, and a bulk change is preceded by a state backup.
  9. Plain ASCII downstream. Curly quotes, typographic dashes and accented characters are converted to plain ASCII before anything is written to SharePoint.