CC_DEV/Docs

Docs

The manual, in plain language, written for the coordinator running it, not the engineer who built it.

PageDocs · the manual
Buildv0.10.0 · 2026-06-10
Cadence Live · every Monday
/01

Overview

Procore-Bot bulk-edits due dates on Procore Action Plan items. You feed it the week’s lookahead: a CSV or an Excel sheet. It reads what’s in Procore today, writes every change it wants to make to a file you can read line by line, and waits. You confirm once. Then it applies the changes, re-checks every item, and logs every write.

It runs on your own computer (or the crew’s always-on box), with your own signed-in Procore session. There are two ways to drive it: this command line, and the web console.

The five stages

1 · Preflight
Checks your file, your session, and your settings before it touches the network.
2 · Plan
Reads what’s in Procore today and writes change_plan.json, a read-only list of every date it wants to change.
3 · Review
You read the plan and confirm once. The only gate, and it’s yours.
4 · Apply
Applies the confirmed plan. Safe to stop and re-run: it never applies the same change twice.
5 · Verify
Re-checks every item and flags anything that doesn’t match what you approved.

What it will never do

  • Sign items off as complete, add signatures, or create and delete sections. These are permanently out of scope. Do them in Procore directly.
  • Change anything but due dates unless you switch it on. Assignee and plan-item writes (add and delete items) exist in the code but ship switched off; each needs an explicit sign-off flag to activate.
  • Store your password. It borrows the session you already signed into (SSO and MFA included) and keeps it on your hardware.
  • Move a date without your confirmation. The plan file is the approval; no plan, no writes.
  • Model schedule logic. No predecessor locks, no dependency math. If a date is governed by a workflow, use Procore directly.
Independence

Procore is a trademark of Procore Technologies, Inc. Procore-Bot is an independent tool built by one project coordinator, not affiliated with or endorsed by Procore.

/02

Getting started

You need three things:

  • Python 3.10 or newer, and the uv package manager.
  • A Procore account with Action Plans Admin permission on the target project.
  • A personal desktop. The tool is single-user and single-machine by design.

Install (one-time per machine)

Four commands: get the code, install the dependencies, install the browser it drives. On Windows, launch.cmd walks the same steps for you.

$ git clone https://github.com/christophercegan2/PROCORE-ACTION-PLAN-AUTOMATION
$ cd PROCORE-ACTION-PLAN-AUTOMATION
$ uv sync --extra dev
$ uv run playwright install chromium

Sign in once

This opens a real Chrome window. Log into Procore with your normal SSO and MFA; when you can see your project list, press Enter in the terminal. The session is saved to your machine, readable by you only. Re-run it only when preflight says it expired, typically every 14-30 days.

$ uv run procore-bot auth

First run

The wizard walks the whole cycle interactively (session, file, plan, confirm, apply, verify) and is the easiest way to learn the shape of it.

$ uv run procore-bot wizard
/03

Weekly runbook

Every lookahead session is the same steps. On a 50-item plan the whole cycle is about ten minutes, most of it your review.

1 · Refresh your sign-in (only when it expired)

$ procore-bot auth

2 · Prepare the lookahead file

Build items.csv from the refined lookahead. Action Plan items have no per-item link, so each row is addressed by the plan’s URL plus the item’s exact title as displayed. A row is a due-date edit unless its action column says otherwise.

ColumnRequiredWhat goes there
plan_urlyesThe Action Plan’s full URL: copy it from the address bar. Rows that share a URL are batched together.
item_titleyesThe item’s title, exactly as displayed in the plan view.
actionnoWhat to do with the row: due (default), assign / unassign, add_item / delete_item.
target_due_datefor dueISO YYYY-MM-DD, or CLEAR to clear the date. Not accepted on add_item: a create can’t seed a date; set it with a follow-up due row.
assigneefor assignThe person’s name as it appears in the project directory. Not accepted on add_item; add them with a follow-up assign row.
sectionfor add_itemWhich existing section the new item lands in.
notesnoWhy the row exists. Carried into the audit log.
# items.csv
plan_url,item_title,action,target_due_date,assignee,section,notes
https://us02.procore.com/.../plans/1001?view=general,RM 101 Pre-Board,due,2026-05-21,,,shifted right per Tuesday walk
https://us02.procore.com/.../plans/1001?view=general,RM 102 Pre-Board,assign,,M. Khalil,,J. Reyes out this rotation
https://us02.procore.com/.../plans/1002?view=general,RM 103 Descoped,due,CLEAR,,,removed from scope per Tuesday walk
https://us02.procore.com/.../plans/1003?view=general,Punch Walk - L05 East,add_item,,,Punch Walk,added to the existing Punch Walk section
https://us02.procore.com/.../plans/1004?view=general,Top-out RM 214 (dup),delete_item,,,,duplicate from template import
Dates are ISO only

21/05/2026, May 21, 2026, and two-digit years are rejected at load. On purpose, before anything touches Procore. Authoring in Excel: format the date column as Text, or set its number format to yyyy-mm-dd.

Clearing a date: put CLEAR (any case) in the date column to set an item back to “no date set.” The plan preview shows it as (clear) and the audit row records it. To restore later, apply the original date in a fresh run.

Deletes are permanent

delete_item removes a record from Procore for good. A rollback re-creates it, but under a new ID. Apply asks for a separate acknowledgement whenever a plan contains deletes; you can switch deletes off entirely in the console’s write policy.

Lookahead lives in Excel? A P6 / MS Project export or a hand-kept sheet can feed the bot directly: no intermediate CSV. See Input files.

3 · Preflight

Checks everything cheap before anything expensive: Python, the browser, your session, the file, the URLs, the dates, and every assignee against the project directory, since Procore 422s any assignment to a user who isn’t active there. Fix and re-run until it’s green.

$ procore-bot preflight items.csv

4 · Plan

Reads the current due date for every row, writes change_plan.json, and prints the diff. Plan is read-only: running it changes nothing in Procore. This is your gate; read it like you’d read a submittal.

$ procore-bot plan items.csv -o change_plan.json
 
Item Current Target Action
------------- ---- ---- ----
RM 101 Pre-Board 2026-05-14 2026-05-21 change
RM 102 Pre-Board 2026-05-14 2026-05-21 change
RM 103 Pre-Board 2026-05-21 2026-05-21 noop
Punch Walk - L05 East - 2026-06-21 add
Top-out RM 214 (dup) 2026-05-12 - delete
 
Summary: 47 change, 12 noop, 1 add, 1 delete, 1 error · Run ID: 7c4a8e2f-…

5 · Apply

Consumes the plan, asks once, then works through it: creates first, then edits, deletes last. Progress prints per item; audit rows are written as it goes.

$ procore-bot apply change_plan.json
About to update 47 items. Type 'yes' to proceed:

6 · Verify

Re-fetches every item and reports anything that doesn’t match what you approved. Run it before you call the update done.

$ procore-bot verify change_plan.json
/04

CLI reference

Every command you’d actually run, in plain terms. Any of them takes --help for the full flag list.

CommandWhat it doesWhen you’d run it
authOpens Chrome so you can sign in; saves the session locallyWhen preflight says the session expired
preflight <file>Checks the file, session, and environment; fails fastStart of every cycle
plan <file> -o <plan>Reads Procore, writes the reviewable plan. Read-onlyAfter the lookahead is refined
apply <plan>Executes the confirmed plan, one confirmation up frontAfter you’ve read the plan
verify <plan>Re-checks every item and reports driftRight after apply
wizardAll the steps, guided and interactiveFirst runs, or whenever in doubt
presets save / runSaves a recurring run’s flags under a name; runs itWeekly scopes, e.g. pre-board-walls
history <plan_url> "<title>"Every recorded touch on one item, across all runs“When did this date move, and who moved it?”
reportWrites a standalone HTML report from the audit logEvidence for the OAC or the pre-board
drift-trendBaseline-vs-actual due-date drift over timeSpotting slip patterns
digest / aging-digestDaily activity summary / weekly aging & blockersThe Monday-morning read
ics <plan>Exports a change plan as a calendar fileDropping dates into Outlook
monitorWatches a tracker for added rows; prompts before dispatching a presetSet-and-check automation
versionPrints bot, Python, and browser versionsBug reports

Flags worth knowing: plan

--allow-missing
Skip rows whose title isn’t in the plan (template drift) instead of stopping. Skipped rows are marked error in the plan.
--shift-working-days N
Nudge every target date by N working days (Mon-Fri, weekends skipped), for a lookahead whose whole schedule slipped. Add --shift-section "Title" to scope it.
--sheet · --header-row
For Excel trackers with scope-specific sheets or preamble rows above the real header.
--external-schedule-config
Read a lookahead XLSX directly via a small column map. See Input files.

Flags worth knowing: apply

--resume --run-id <uuid>
Continue a stopped run. Items already applied are looked up in the audit log and skipped.
--reverse
Roll a run back: swaps current ↔ target for every change and writes a fresh run id.
--workers N
Parallel browser workers, one plan per worker at a time.
--no-confirm
Skips the confirmation prompt. For saved presets and automation only.

Input files

items.csv is the everyday input. Seven columns (two always required), addressed by plan URL + exact item title; action defaults to a due-date edit. The full column table is in the runbook.

External lookahead (XLSX): a 3-week lookahead exported from P6 / MS Project / Smartsheet, or a hand-kept forecast sheet, consumed directly. A small TOML file maps your column headers to the bot’s fields (plan_url, item_title, target_due_date, optional notes) and names the sheet and header row. Columns are matched by header name, so your column order is free.

$ procore-bot plan lookahead.xlsx --external-schedule-config config/p6_external_schedule.toml

change_plan.json is the written plan. Output of plan, input of apply and verify. It carries the run id and every proposed change; the file, not a dialog box, is what you approve.

Never hand-edit a plan

Need different dates? Run plan again and get a fresh plan and run id. Editing a plan file and resuming is the one way to sneak an unreviewed change past the gate; the bot detects the mismatch (target-drift guard) and stops.

/05

Run reports

Every run leaves a paper trail on disk, and a few commands turn that trail into something you can hand to someone.

What a run leaves behind

change_plan.json
The plan exactly as approved, in ~/.procore-bot/runs/<run_id>/.
log.jsonl
Every event in the run, timestamped, machine-readable.
resume.sh
The exact command to continue the run if it was interrupted.
data.sqlite
The audit database: every run, every write. See Audit trail.

Show your work

report writes a standalone HTML file from the audit log: counts, timings, and outcomes, accounted for line by line. It opens anywhere; no bot required to read it.

$ procore-bot report

history answers the smaller question (what happened to this item) across every run that ever touched it.

$ procore-bot history <plan_url> "RM 101 Pre-Board"

digest and aging-digest summarize activity: a daily read and a weekly aging-and-blockers read. Both print to the terminal, and can post to a Slack webhook if you configure one.

$ procore-bot digest
$ procore-bot aging-digest
/06

Harvest sidecar

The harvest sidecar exists for one situation: the bot runs on a machine that isn’t the one where you’re signed in (say, the crew’s always-on mini PC) and you’d rather not type your password anywhere new.

A small Chrome extension watches your normal, signed-in Procore tab. On demand (or automatically, if you opt in), it pushes that session (cookies, never credentials) to the bot’s box over an authenticated channel: Cloudflare Access in front, plus a shared secret. Your password and MFA never leave your browser.

  • The session lands on hardware you control; nothing sits on the open internet.
  • At-rest encryption for the stored session is available as an opt-in.
  • A keep-alive loop stretches the session between Mondays; the extension badge counts down to expiry.
  • Every push is logged to its own audit table, including whether the Cloudflare identity check passed.
# the sidecar is a small FastAPI service behind Caddy + cloudflared
$ python -m uvicorn procore_bot.harvest.service:app --host 127.0.0.1 --port 8502
Running everything on one machine?

Then you don’t need any of this. procore-bot auth alone is the whole session story.

/07

Audit trail

Every write is remembered in a single SQLite file: ~/.procore-bot/data.sqlite. The log only grows. The bot never edits or deletes a row. Undoing something is a new run (apply --reverse), which writes its own rows and shows up in Procore’s own audit log as its own event.

What’s in it

TableWhat it records
runsOne row per run: id, when, what scope.
audit_entriesEvery plan / apply / verify touch on an item: who ran it, what changed, the value before and after. Keyed by plan URL + item title.
apply_intentsThe two-phase safety net: a row is written before each write fires and updated after, so a crash mid-write can’t cause a double-apply on resume.
harvest_eventsEvery session push through the sidecar, with the identity-check result.

Looking things up

$ procore-bot history <plan_url> "<item title>"
# or query the file directly
$ sqlite3 ~/.procore-bot/data.sqlite \
"SELECT * FROM audit_entries WHERE plan_url = ? AND item_title = ? ORDER BY timestamp"

Schema upgrades are automatic and additive: opening an old database with a new build migrates it in place, and your history stays.

Backups

The audit database is the record. If the trail matters to you, back up data.sqlite like any other project document.

/08

Troubleshooting

The short version: almost everything is recoverable with --resume, and nothing the bot does is hidden from the audit trail.

A run stopped partway

Resume with the same run id. Items already applied are looked up in the audit log and skipped. That’s the idempotency contract; resuming is always safe.

$ procore-bot apply change_plan.json --run-id <uuid> --resume

Stopped it with Ctrl+C? The bot writes an executable resume.sh into the run folder with the exact command:

$ bash ~/.procore-bot/runs/<run_id>/resume.sh

Rolling back

Swaps every applied change back (current ↔ target) under a fresh run id, so the rollback is itself on the record.

$ procore-bot apply change_plan.json --reverse

Common symptoms

SymptomLikely causeFix
Preflight: session expiredThe SSO window lapsedprocore-bot auth
Preflight: chromium not foundBrowser not installedplaywright install chromium
Apply: target drift detectedThe plan changed since the run startedRun a fresh plan; never hand-edit a plan file
title not found in enumerated planTracker row isn’t in Procore (template drift)Fix the tracker, or pass --allow-missing to skip it
Apply: throttled (HTTP 429)Procore is busyIt backs off on its own; if persistent, set PROCORE_BOT_PAUSE_MS=5000 and resume
3 straight failures at the start of applyProcore’s UI changed (selector regression)Refresh selectors (≈30 min), then resume
Verify: drift on N itemsSomeone edited between apply and verifyRe-plan and re-apply if the change is intended
Assign row fails with 422Assignee inactive (or missing) in the project directoryPreflight catches this; fix the name or ask the PM to re-activate the user
Every item errors, session validNo Action Plans Admin on that projectConfirm permission with the project admin

When not to use it

  • A single one-off edit: the Procore UI is faster for one item.
  • Dates governed by a workflow: predecessor locks, baseline freezes. The bot doesn’t model dependencies.
  • Real-time sync from an external schedule: feed it a file instead.