← PanOpticon
SCOPE
Scope
Users, navigation, scheduling direction, and multi-user data sharing. An internal planning document, written against v2.16.0.
SECTIONS

PanOpticon — Scope

Users, navigation, scheduling direction, and multi-user data sharing Version 0.3 · August 2026 · against PanOpticon v2.16.0

Supersedes docs/multi_user_scope.md (v0.1). Section 7.2 corrects a factual error in that document. Section 3 is revised from v0.2 following confirmation of who actually uses this.


1. Purpose of this document

Three questions, addressed together because they interact:

  1. Can PanOpticon be made easier to use without removing what daily users rely on?
  2. Can it become a scheduling and planning aid, not only a tracking one?
  3. Can several people contribute to one job's data without the constant file-shuffling that manual tracking already inflicts?

They interact because sharing and scheduling both add concepts — devices, sync, target dates, sequence — to a program that is already dense. Either built carelessly makes the usability problem worse, not better.

Findings below come from parsing the v2.16.0 source and from running the concurrency behaviour directly, not from reading the code and estimating.


2. What PanOpticon is today

A single-user Windows desktop application. Scope is imported from CAD pre-fab exports; field progress is ticked off against it; budget hours come from the accounting variance report; the gap between them is surfaced rather than smoothed over.

The design commitment throughout is that disagreements are shown, never silently resolved. That principle should survive everything proposed here.

2.1 Measured size

ModuleLinesRole
gui.py4,802All six screens and their dialogs
db.py1,058Schema, 13 tables, all persistence
importer.py523CAD folder discovery, Column ID → phase mapping
hours.py491Budget allocation, connection weighting, projections
metrics.py481Progress, daily series, burn-up
excel_out.py490Workbook export and write-back
checklist_pdf.py462Field checklist round-trip
cli.py457Headless operations
gaps.py443Reconcile checks
fittings.py405FITTINGS / BOM PDF parsing
variance.py349Variance report parsing
Others875parser, writer, scanner, picker
Total10,836

gui.py is 44% of the codebase. That is the first structural observation worth acting on, and it shows up in the usability findings below.


3. Who uses this

Confirmed, and it changes the design brief materially:

RoleUses PanOpticon for
SuperintendentLook-ahead, crew allocation, budget position
Field Manager / Crew Leader IIRecording installed work, tracking their own crews
Project Engineer (occasional)Scope import, variance reconciliation

Not field labourers. Everyone entering data is a competent professional who uses the tool regularly and knows the trade.

That invalidates part of v0.2. The goal is not to make PanOpticon novice-proof — nobody here is a novice, and hand-holding would slow down people who use this every day. The real problems are different:

Simplification here means fewer places to look, not fewer capabilities.


4. Navigation

4.1 What changed (implemented)

Six top-level tabs became four:

BeforeAfter
Scope · Dashboard · Checklist · Productivity Log · Takeoff · ReconcilePlan · Production · Takeoff · Data

Plan = Progress · Daily · Hours Production = Log Work · History · Calendar Data = Scope · Reconcile

Rationale:

Sub-tabs are flat — no third level of tabs. The level selector and stat strip appear only above the level-scoped views (not Hours, which is scoped by phase code and needs the height for its table), and the log filters only above History and Calendar.

Regression coverage: simtest2.py now asserts the tab structure, the absence of a third tab level, every jump target, header visibility per sub-tab, and that the Scope tree retains every column the removed Systems tab carried.


5. GUI analysis

5.1 Measured complexity

Measured before the §4 merge — six top-level tabs, nine sub-tabs. Now four top-level tabs and seven sub-tabs, with the same controls:

ScreenControlsNotes
Checklist19The daily driver — and the densest screen
History (was Productivity Log)113 combo filters, 2 date pickers
Dashboard11Plus 4 sub-tabs
Scope7
Reconcile4
Takeoff3

Fourteen tables/trees, the widest carrying nine columns. Seventy QMessageBox call sites. Eighteen tooltips across the entire application.

Those last two numbers are the finding. PanOpticon explains itself almost entirely through modal dialogs that appear after you act, and almost not at all before you act. A new user learns the program by being interrupted.

5.2 The core problem: everything has equal weight

Not that there is too much on screen — a superintendent can handle a dense screen — but that nothing on it is ranked.

The daily task, recording installed pieces, sits on a screen offering twelve other buttons: export for iPad, import checklist, open spool sheet, open spool map, select-all, clear, expand, collapse, date stepper, crew size, hours per man. "Log installed" is styled identically to "Export for iPad…", which is used perhaps monthly.

The information is correct and wanted. It is the ranking that is absent: the hourly action looks the same as the monthly one.

5.3 Specific findings

F1 — The daily task is not the visually dominant one. "Log installed" is one button among thirteen on the Checklist screen, with no visual weight distinguishing it from "Export for iPad…".

F2 — Empty states teach nothing. Only four empty-state strings exist in 4,802 lines, and they state a fact ("No systems on this level yet") without offering the next action. A first launch shows four tabs of empty tables and no indication that everything begins with Import Pre-Fab Spool Folders.

F3 — Discoverability depends on tooltips that mostly do not exist. Eighteen tooltips for roughly 55 interactive controls. Features added recently — crew assignment by double-click, right-click column management, right-click bulk assign — are discoverable only by accident or by reading the basis text beneath the Hours table.

F4 — Terminology assumes the domain and the tool. "Uncommitted Remaining", "PAP Changes", "Proj vs Est Hours", "Crew days", "Over / Under". A pipefitter knows the trade but not necessarily this report's vocabulary. The Hours tab's basis paragraph does explain these, but it sits below the table it explains.

F5 — Destructive and routine actions look identical. "Clear assignments" and "Reset to defaults" sit in the same button row, in the same style, as "Add crew…". Confirmation dialogs catch this, which is correct — but the visual language does not.

F6 — Nine-column tables with no column control except on Hours. The Hours tab now supports hide/reorder/reset. Scope (9 columns), Systems (9) and History (8) do not. The capability exists and is proven; it is simply not applied consistently. (The Dashboard's Systems table, also 9 columns, has since been removed as a duplicate — see §4.1.)

F7 — No undo outside the log. Reversal exists for install entries, correctly. But crew assignment, column layout, config changes, and scope deletion have no undo — only confirmation prompts.

5.4 What is already good, and should not be disturbed

Stated so that a redesign does not discard it:

5.5 Proposal

Revised for the confirmed audience: these are daily professional users, so every item below removes friction rather than adding guidance.

P1 — Primary-action styling. One visually dominant button per screen — the thing that screen is for. "Log installed" on Log Work; "Import Pre-Fab Spool Folders" on Scope. Secondary actions recede. Styling, not structure.

P2 — Empty states that start the workflow. Every empty table gets a sentence and a button. Not hand-holding — a first launch currently shows empty tables with no indication that everything begins with a scope import, and that costs a superintendent ten minutes once.

P3 — Column control everywhere. Apply the Hours tab's existing hide/reorder/reset to the Scope tree, the History table, and Takeoff. Each person tunes their own density; nobody decides centrally what to remove. Directly addresses "dense in the wrong places" without losing anything.

P4 — Surface the hidden interactions. Double-click to assign a crew, right-click for bulk assign, right-click headers for columns. All undiscoverable today. A short affordance — a hint row, a context-menu cue — costs little and unlocks features already built.

P5 — Destructive actions styled as destructive. "Clear assignments" should not look identical to "Add crew".

P6 — A glossary, reachable and inline. "What the numbers mean", plus ? on dense headers. Content largely exists in the Hours basis paragraphs already; it is placement that is wrong.

P7 — Split gui.py. 4,802 lines in one file makes every change riskier than it should be. One module per tab, mechanical and behaviour-preserving.

Ordered by value per unit of effort: P2, P1, P3, P4, P5, P6, P7.

(The "Today screen" proposed in v0.2 is withdrawn. With Plan and Production merged, a fifth summary screen would be a fourth view of data already visible — the mistake the merge just corrected.)


6. Direction: scheduling and planning

The stated goal is for PanOpticon to become a scheduling and planning aid, not only a tracking one. This is a genuine change in kind, so it is worth being precise about the gap.

6.1 What PanOpticon already has

More than half the foundation, which is why the goal is realistic:

PanOpticon can already answer "how long will the remaining work take this crew".

6.2 What is missing for scheduling

GapWhy it matters
No target datesEvery projection counts forward from today. There is no "this must be done by the 14th", so there is no such thing as behind — only how long left
No sequence or dependenciesUnderground before slab, risers before branches. PanOpticon treats remaining spools as an unordered pool
No look-ahead windowThree-week look-ahead is the standard planning artefact in this trade. PanOpticon has no concept of a window
No crew calendarCrews have a size but no availability — no holidays, no "Ruiz is on another job Thursday"
No plan-vs-actualWithout a recorded plan, there is nothing to compare actuals against

6.3 The one that unlocks the rest

Target dates on scope. A due date per system (or per spool) converts every existing projection from "11 days of work" into "11 days of work, due in 8 — three days short."

That single addition turns the Hours tab from a calculator into a schedule, and it requires no new arithmetic: the durations, crew sizes, working-week model and finish dates already exist and are tested. It is a field, a way to set it, and a comparison.

6.4 A staged path

S1 — Target dates. A due date per system, entered manually or imported. Hours tab gains Due and Slack columns; a crew running past its date goes red. Small, and immediately useful.

S2 — Look-ahead view. A three-week window listing what each crew should be on, derived from assignments, durations and dates. Printable — the artefact superintendents already produce by hand.

S3 — Sequence constraints. "This system cannot start until that one finishes." Enables realistic dates instead of everything starting today.

S4 — Crew availability. Per-crew calendars, so durations account for a crew being elsewhere.

S5 — Plan-vs-actual. Snapshot the plan; compare against what happened. The scheduling equivalent of what Reconcile does for scope.

S1 and S2 deliver most of the practical value. S3–S5 are real projects.

6.5 Honest caveat

PanOpticon will not become Primavera or MS Project, and should not try. Its advantage is that its durations come from measured production on this job rather than a planner's estimate. The right ambition is a look-ahead tool that is right about durations — not a general-purpose CPM scheduler.


7. Multi-user data sharing

7.1 The requirement

Several people contributing to one job's data, "without complications like constantly updating" — meaning no manual export/import ritual, no merge prompts, no wondering whose copy is current.

7.2 Correction to the v0.1 scope document

The previous document's central premise was:

"PanOpticon's log is already append-only… Nothing is ever edited in place or deleted."

This is not accurate as of v2.16.0. db.delete_log_entry() hard-deletes rows from the log table, and cascades to delete related reversal rows. Reversal is append-only; deletion is not.

This matters because event-journal merging depends on it. Deletion does not make the approach impossible, but it means deletion must itself become an event ("event X retracted") rather than a row disappearing. A deleted row cannot propagate — the other machine simply never learns the row is gone, and re-adds it on the next sync.

7.3 A second obstacle the v0.1 document did not identify

Events reference pieces by machine-local integer:

piece_id INTEGER PRIMARY KEY AUTOINCREMENT   -- assigned per database
log.piece_ids TEXT                           -- JSON array of those integers

Piece 4,182 on one laptop is a different physical piece from 4,182 on another. Unioning event journals without translation would attach installs to the wrong pipe — silently, and in a way that still balances arithmetically. That is the worst possible failure for this product.

It is solvable, because a natural key already exists:

UNIQUE(system_id, spool_no, item_no, csv_row)

Every event must therefore be translated to and from that natural key on the way in and out of a journal. This is real work — it is not a schema annotation — and it needs to happen before history accumulates.

7.4 Measured concurrency behaviour

Run against v2.16.0 rather than assumed:

SettingValueConsequence
journal_modedeleteDefault rollback journal
busy_timeout5,000 msAlready set — waits rather than failing instantly
synchronous2 (FULL)Durable

Direct test of two writers:

second writer: BLOCKED -> database is locked

Readers were not blocked during a write in either delete or wal mode at this database size, so WAL is not the unlock some guidance suggests. The write lock is the constraint, and it is a correct constraint — it is what prevents corruption.

7.5 Options assessed

Option A — Shared database file on a network drive

Point everyone at Z:\…\panopticon.db.

Not recommended. SQLite's own documentation states that file locking is unreliable over SMB/NFS. The failure mode is not an error message; it is a damaged file, discovered days later. For a product whose name means "the truth", this fails in exactly the wrong direction.

Option B — Database in OneDrive/Dropbox/SharePoint

Strongly not recommended. Sync clients replicate whole files, not transactions. Concurrent edits produce a "conflicted copy" and one person's work is discarded with no indication which copy was right.

Option C — Per-device event journals in a shared folder

Each device writes only its own journal file; nobody writes a file another person is writing, which removes the locking and conflict failures above. On launch and on demand, PanOpticon reads the others and applies unseen events.

Requires the two fixes in §7.2 and §7.3 first.

Option D — Hosted database server (PostgreSQL)

True concurrent multi-user, real-time, proper accounts and permissions. Requires hosting, IT involvement, ongoing cost, and network dependence — PanOpticon stops working in a basement with no signal, which today it does not.

Option E — One writer, many readers

One person owns entry; everyone else gets a scheduled Excel export to a shared folder. The Excel write-back already exists, so this is close to free.

Not sharing in the strict sense, but if the real need is visibility rather than contribution, it delivers most of the value this week rather than this quarter.

7.6 Comparison

A: Net shareB: OneDriveC: JournalsD: ServerE: Export
Concurrent writersCorruptsLoses dataYes (eventual)Yes (real-time)No
OfflineYesPartlyYesNoYes
Data leaves premisesNoYesNo (or optional)DependsNo
InfrastructureNoneNoneShared folderHosting + ITNone
EffortSubstantialLargeVery small
Risk of silent wrong dataHighHighLowLowLow
RecommendedNoNoYes, stagedLaterYes, now

7.7 Recommendation

Immediately — Option E, if the need is largely visibility. Scheduled export to a shared workbook, using machinery that already exists.

Foundation work now, regardless of what follows:

  1. Add UUID event_id and device_id to every log row, with UTC timestamps.
  2. Convert delete_log_entry to a retraction event rather than a row deletion (§7.2).
  3. Store piece references in events by natural key, not local integer (§7.3).

These cannot be retrofitted cleanly onto history that accumulates without them, and each is independently defensible as a correctness improvement even if sharing is never built.

Then Option C, in phases, once the number of people actually entering data is known.

Option D only if the organisation outgrows C — and the event model migrates to it rather than being thrown away.

7.8 On "without constantly updating"

This deserves a direct answer rather than a reassuring one.

Option C can sync automatically on launch, on a timer, and on demand, so nobody presses a Sync button. What it cannot do is make two people's screens agree within seconds. If someone logs Level 4 while you are looking at Level 4, you will not see it until the next sync cycle.

Only Option D gives second-by-second agreement. If the expectation is "we both see it instantly", C will disappoint, and it is better to know that before it is built than after.


8. Open decisions

  1. How many people enter data, versus only view it? This single answer decides between Option E and Option C, and it is the cheapest question to answer.
  2. Is the priority contribution or visibility?
  3. Is device-name attribution sufficient, or are named users required?
  4. Shared folder location — Z: project folder, or elsewhere?
  5. One journal set per job, or across all jobs? Per job is cleaner to archive at closeout.
  6. For scheduling: do target dates already exist somewhere importable (a P6 schedule export, a spreadsheet), or would they be entered by hand? This decides whether S1 is an import or a data-entry feature.
  7. Is the three-week look-ahead (S2) the artefact that would actually get used, or is something else the working document?

Now, low risk, high return P3 empty states · P2 primary actions · P6 destructive styling · Option E export

Next P4 column control everywhere · P5 glossary · Sharing foundation work (§4.7 items 1–3)

Then, given answers to §8 S2 look-ahead view · P7 split gui.py · Option C phased sharing

Later, only if outgrown Option D server


10. Risks

RiskMitigation
Simplification removes something relied onNothing is removed; ordering and defaults change. Column control is per-user
Sharing built before piece-identity fixInstalls attach to wrong pipe, silently. §7.3 is a hard prerequisite
Deletion not converted to a retraction eventDeleted entries resurrect on next sync. §7.2 is a hard prerequisite
Expectation of real-time sync from Option CStated plainly in §7.8 before building
Shared folder unreachable in the fieldWork continues locally; sync resumes
Clock skew between machinesUTC timestamps; ordering by event ID, not wall clock
gui.py growth makes changes riskyP7, mechanical and behaviour-preserving
Scheduling scope-creeps toward a CPM tool§6.5 — the advantage is measured durations, not planning breadth
Target dates entered once and never maintainedS1 is only useful if dates are kept current; confirm ownership before building
Regression during refactorsimtest.py (31 checks) and simtest2.py (81 checks) run against any change