Skip to content

PointSav Documentation

The engineering library for the PointSav platform — operating systems and services for regulated businesses that own their data, their AI, and their record-keeping outright. Where the monorepo holds the code, this wiki holds the reasoning: architecture, services, security, and the governance commitments that bind future development.

Generate a payroll register

Prerequisites

  • A working Rust toolchain (see How to install the development toolchain)
  • A checkout of the workspace containing the construction crates — the payroll crate is a member of it, and reads two reference files out of the construction crate's own directory at a path resolved when it compiles
  • A construction data directory holding work_packages.csv
  • Write permission on the output directory

No services need to be running. This is a batch task on the command line, and the command line is the whole interface — there is no console screen, no F-key slot, and nothing to click.

One prerequisite is not configurable and will surprise you: the output directory is a hard-coded absolute path with no environment-variable override. The run creates it and fails outright if it cannot. The input data directory is redirectable; the output is not. That asymmetry is the reverse of the sibling accounting tool's, and it is a real gap rather than a design position.

Purpose

Produce the Payroll Register (by Division) — a single working schedule of budgeted labour hours and crew size, one row per construction division, as HTML and PDF.

Read the next paragraph before running anything, because the report's name promises considerably more than the report delivers.

This command computes no pay. It does not calculate gross pay, net pay, or any deduction. It does not determine a pay frequency, a pay date, or a remittance schedule. It does not read a timecard — the hours it aggregates are budgeted hours from work-package estimates, not hours anyone worked. It has no concept of an employee: its rows are divisions, and the crew size beside each one is a planning assumption, not a headcount. The columns headed Pay Freq. and Gross Pay exist on the page and every cell in both of them is an em dash.

That is deliberate, and the document says so on its own face. The gross-to-net computation and the pay-frequency data model are both explicitly undesigned at this stage, and the renderer prints a dash rather than a fabricated number. What the register is genuinely useful for is what its two populated columns say: how many budgeted labour hours each division carries, and what crew size the plan assumes for it.

For the design this report is a first slice of, see tool-payroll — jurisdiction-aware payroll and statutory remittance. For the tool the hours come from, see tool-construction — construction cost, schedule, and quality ledger.

Procedure

1. Point the tool at the construction data directory

export TCO26_DATA_DIR=/data/construction/example-project

This is the same variable the construction reporting binary uses, and it behaves the same way: unset, the binary falls back to a hard-coded absolute path belonging to the deployment it was first built for, and the run fails on a file read naming a directory you have never heard of.

2. Confirm the four inputs

The run reads four CSV files from three different places. Two arrive with the checkout, one comes from the directory you just exported, and one lives in the payroll crate's own data directory.

File Where it comes from What is read from it
division_crosswalk.csv The construction crate's tracked reference data Cost-code prefix and division name
crew_assumptions.csv The construction crate's tracked reference data Division name and crew size
work_packages.csv TCO26_DATA_DIR Cost code and budgeted labour hours
wage_payment_rules.csv The payroll crate's own tracked reference data The jurisdiction row printed in the note

division_crosswalk.csv:

uniformat_prefix,csi_division,division_name

crew_assumptions.csv:

csi_division,division_name,crew_size,hours_per_day

wage_payment_rules.csv:

jurisdiction_code,max_pay_period_days,max_days_to_pay_after_period_end,day_counting,remitting_authority,comp_authority,source_ref,effective_from

From work_packages.csv — a wider file the construction tool owns — this run reads exactly two fields: cost_code and labor_hours_budget. Everything else in the row is ignored.

Three details are worth knowing before a first run:

  • hours_per_day is loaded and never used. The crew-assumptions loader parses it into memory; this report consumes only crew_size. It is not a column you need to get right for this task.
  • A cost code joins to a division by longest matching prefix. Each crosswalk row declares a prefix; a work package's cost code is matched against every prefix and the longest match wins. A code matching no prefix at all joins to nothing — see the third verification check below for why that matters more than it sounds.
  • The jurisdiction is compiled in. There is no flag, no environment variable, and no column anywhere that selects which jurisdiction row is read. One jurisdiction code is a constant in the binary, and only that row is looked up.

3. Run the binary

From the workspace root:

cargo run -p tool-payroll-tco-26

That is the complete command. There are no flags, no subcommands, and no positional arguments — the binary does not parse a command line at all, so there is no --help to consult, no --jurisdiction to override, and no way to render only part of the report. Every configurable thing it has is the one environment variable from step 1.

4. Read the console line

A successful run prints exactly one line:

[payroll_register] 9 division(s), 4820 total budgeted hour(s) — written to <output directory>

The counts are invented for this guide; the shape is real. Both numbers are worth reading rather than skipping — they are the only summary the run produces, and the division count is the first thing that reveals a broken crosswalk.

Expected outcome

Two files in the compiled-in output directory, which the run creates if it is absent:

File What it is
payroll_register.html The register as a web page
payroll_register.pdf The same register as a print document

The document has three parts: a masthead carrying the deployment's own project label, the report title, and the division count; a Basis of preparation note; and one table.

The table has five columns — Division, Crew Size, Budgeted Hours, Pay Freq., Gross Pay — and repeats its header across pages. Two of those five are populated. The register is typeset as a working document rather than as a filed statement, which is the correct classification for a schedule of budgeted figures.

The Basis of preparation note states in the document itself that crew size and budgeted hours are real and sourced from the construction tool's work-package data, that pay frequency and gross pay are not shown because neither has been designed, and that no timecard or payroll transaction has ever been recorded for the project. A second paragraph, present only when the jurisdiction row is found, states that jurisdiction's wage-payment ceiling in days, whether those days are counted as calendar or working days, which authority administers remittance, which administers workers'-compensation reporting, and the citation behind all of it.

That second paragraph is a statement of the rule, not an application of it. Nothing in this run computes a pay date, and nothing checks one against the ceiling.

Verification

Check the division count against the crosswalk. The number in the console line is how many distinct divisions received at least one matched work package. If it is lower than the number of divisions you expect to see work in, the crosswalk is not matching what you think it is.

Check the total against the table. The total budgeted hours in the console line is the sum of the Budgeted Hours column. It should also agree with the man-hours figure the sibling construction status report produces from the same work-package data — the join and aggregation here deliberately mirror that report's own logic so the two can never quietly disagree about the same underlying numbers. If they differ, one of the two has drifted, and that is a finding worth chasing.

Sum labor_hours_budget yourself and compare. This check is not optional, and it is the only way to catch the report's one silent failure. A work-package row whose cost code matches no crosswalk prefix, or whose labor_hours_budget is blank or does not parse as a number, is dropped from the aggregation with no warning, no counter, and no mention anywhere in the console line or the rendered document. The register will look complete. Add up the column in your source file: if your total is higher than the console line's, the difference is dropped rows, not an arithmetic error.

Read the em dashes as two different facts. A dash under Pay Freq. or Gross Pay means the platform does not compute that quantity at all. A dash under Crew Size means something narrower and more actionable: no row in crew_assumptions.csv has a division name matching that division. The renderer refuses to print 0 there, precisely so a missing assumption cannot be mistaken for a crew of nobody.

Confirm the jurisdiction paragraph is present. If the Basis of preparation note ends after its first paragraph, the jurisdiction lookup found nothing and the document has silently lost its entire regulatory disclosure. See the edge cases below.

What this task does not do

  • It does not compute gross pay. No wage rate is read from anywhere. The column is structural.
  • It does not compute net pay or any deduction. Gross-to-net computation — tax brackets, statutory deduction formulas — is explicitly out of scope for this build and is not partially implemented.
  • It does not determine a pay frequency. No field carrying a per-crew or per-employee pay frequency has a home in any schema this run reads, which is why the column is a dash rather than a default.
  • It does not compute or enforce a pay date. The jurisdiction's wage-payment ceiling is printed as text in a note. Nothing derives a pay date, and nothing checks one against that ceiling.
  • It does not remit anything, or compute a remittance schedule. Remitting and workers'-compensation authorities are named in the note as facts about the jurisdiction. No schedule is calculated.
  • It does not read timecards. Every hour in this report is a budgeted estimate attached to a work package. No hour anyone actually worked appears anywhere.
  • It is not per-employee. There is no employee record, no roster, and no name in this pipeline at any point. Rows are divisions.
  • It does not select a jurisdiction. One jurisdiction code is a compile-time constant; an operator in another jurisdiction currently needs a code change, not a configuration change.
  • It writes no ledger entries. The run reads four files and writes two.

Edge cases

  • Any missing input file aborts the run with read <path>: <error> and a non-zero exit. Nothing is written. The message is a raw panic rather than a formatted error — it names the path, which is the part you need.
  • A short row in either reference file aborts the run the same abrupt way. Both reference loaders index fixed column positions, so a row with fewer fields than expected is an index failure, not a skipped line.
  • A comma inside a division name will break the crosswalk. The work-package reader handles quoted fields; the two reference loaders split on plain commas and do not. A division name containing a comma silently shifts every field after it.
  • An unrecognised day_counting value in the jurisdiction table aborts the run naming the offending value. Only two spellings are accepted.
  • No row for the compiled-in jurisdiction is not an error. The run succeeds, the two files are written, and the Basis of preparation note simply omits its jurisdiction paragraph. This is the most dangerous failure in the tool, because the document looks finished and has quietly dropped its entire regulatory disclosure. Check for the paragraph rather than assuming it.
  • Divisions are ordered alphabetically by name, not by division number. If you expect a numeric ordering, the report is not wrong — it is sorted on the other key.
  • Output files are overwritten in place. No versioning, no timestamped directory, no prompt, and no environment variable to send them elsewhere. Copy anything you need to keep before re-running.

Rollback

Nothing to undo in the source data: the run reads four files and never writes to any of them. Its only writes are payroll_register.html and payroll_register.pdf. Delete them, or re-run to replace them.

Next steps

See also

Cite this record: /wiki/generate-a-payroll-register — revision 8fff5a87, last updated 1 September 2026.

Important Information

Corporate structure. PointSav Digital Systems ("PointSav") is currently a trade name of Woodfine Capital Projects Inc. ("Woodfine"), planned to become a wholly-owned Woodfine subsidiary upon incorporation. PointSav does not itself offer, sell, or solicit any security. Any securities offering associated with Woodfine's real-property direct-hold solutions is made exclusively by Woodfine, and only by means of the applicable Private Placement Memorandum.

No investment advice. This wiki's content is provided for engineering, operational, research, and development purposes. Nothing on this wiki constitutes investment advice or a solicitation to invest in any Woodfine partnership or direct-hold solution.

Intellectual property. The PointSav name, trade name, wordmark, and marks, together with all current and future PointSav- and Totebox-branded products, services, and offerings — and the software, source code, documentation, design system, and all related materials — are proprietary to Woodfine and its affiliates, except for components identified as open source. No rights are granted except as expressly set out in a written license or agreement. The full trademark notice appears in the footer of every page on this site.

Open source components. Portions of the platform are made available under permissive open-source licenses identified in the accompanying repository. Use of those components is governed by their respective license terms.

No warranty; informational use. Content on this wiki is provided for general informational purposes only and does not constitute a representation, warranty, or commitment with respect to product functionality, availability, pricing, or roadmap. Some articles describe planned or intended features, capabilities, and milestones — language such as "planned," "intended," "targeted," "may," and "expected" marks this forward-looking content, which is subject to change and does not constitute a commitment regarding future performance.

Confidentiality. Where an article describes an operational or deployment detail that is not intended for public disclosure, that article is not published on this wiki. Content here is general-purpose engineering documentation, not customer-specific configuration.

Jurisdiction. Woodfine Capital Projects Inc. is organized in British Columbia, Canada. References to the Sovereign Data Foundation on this wiki describe a planned or intended initiative only, not a current equity holder or active governance body.

Changes to this notice. PointSav may update this notice from time to time; the version posted on this page governs.

Not a filing system. This wiki is not a securities filing system, an electronic disclosure repository, or a substitute for SEDAR+ or any other regulatory filing system. Formal securities filings are made through the applicable regulatory filing system, not through this wiki.

Full disclaimer. This notice supplements, and does not replace, the full Disclaimers article. In the event of any conflict, the full Disclaimers article governs.

Read the full disclaimer →