Repository navigation
Releases: ryanduguid/au-fpa-pack
Release list
v0.3.0
v0.3.0
Breaking for code and configurations written against 0.2.0:
- Requires Python 3.14 or later (#172). 0.2.0 remains the last release for Python 3.12 and 3.13.
fetch_rba_seriespicks each month's value by observation date instead of file order, keeping the latest date in the month, and raisesValueErrorwhen one date carries two different values (#175). Identical duplicate dates are still accepted.- Forecast evidence extraction refuses incomplete selected evidence and non-finite totals, and
score_forecastrequires a finite, positive weight total (#182). Missing values used to disappear during reduction, and two weights of 1e308 could erase nonzero errors. A refused portfolio candidate now fails its fold while the other folds continue.
Added:
XeroReport.account_identities()andpyfpa.memory.reimport.compare_reimport()compare account identities, mappings and source metadata across Xero re-imports (#179). Account codes keep leading zeros, and the comparison reports additions, removals, renames, tracking changes, mapping changes and coverage gaps without changing amounts or company memory.
Fixed:
write_onboarding_outputsrefuses a linkeddecisionsfolder that leads out of the workspace, and replaces files through a sibling temporary file, so a failed overwrite no longer truncates an existing business profile (#173).- A forecast snapshot whose assumptions cannot be written no longer creates an empty file or erases an existing snapshot during an explicit overwrite (#177).
v0.2.0
Breaking for code and configurations written against 0.1.2:
- Requires Python 3.12 or later, with pandas 3.0.5, PyYAML 6.0.3 and openpyxl 3.1.5 as the dependency floors (#159). 0.1.2 remains the last release for Python 3.11.
- The days-based working-capital model refuses a configuration that implies negative customer receipts, purchases or supplier payments, naming the month, the flow and the amount, and
model-exportwrites no workbook for it. A DSO of 60 days with revenue tripling in a month used to report a smaller operating cash flow instead (#139). Use a collection profile or the replenishment basis below where the days model cannot follow the business. - Forecast configuration rejects NaN and infinity (#135), a receipt shift rejects non-finite amounts and keeps cash unchanged when its source and destination month match (#136), and seasonality that overflows its total or growth that overflows raises
ValueErrorinstead of erasing revenue or producing infinite cash (#137). - Removed public names nothing read:
GstAssumptions.agent_lodgment(a YAMLagent_lodgmentkey is now ignored),XeroReport.gst_inclusive,XeroReport.unmapped_trackingand theconnector-validate --timeoutflag (#134).
Added:
review_forecaststructural controls over a monthly forecast frame (#140), an opt-in income-year tax provision (#141), receivables collected by a stated profile (#142) with bad debts and later write-offs (#150), and an opt-in replenishment basis for inventory (#151).pyfpa.au.payroll_settlementsettles payroll costs on their own dates (#143), with due-date rules for PAYG withholding (#144), the super guarantee under Payday Super and the quarterly regime (#145) and monthly payroll tax lodgers (#146), and PAYG instalments for quarterly payers (#149).- Ridgeline cash decisions with a 12-cell receipt-delay and hiring grid (#166), and a Lumbridge close handoff that reconstructs the opening payable from its own fabricated bills, credits and settlements (#170).
Fixed:
model-exportrefuses to write over its own configuration, including through links (#154); correction slugs cannot leave the corrections directory (#155); experiment saves no longer overwrite a competing save of the same name (#156); depreciation evidence refuses a non-booleansynthetic_input(#157); registry loaders report broken paths and non-mapping YAML instead of returning empty records (#158).- The Lumbridge briefing's figures are tied to their sources by tests (#138), the learning workflows keep Xero API data out until the developer terms are checked (#148), and
docs/ai-register-entry.mdgives supplier information for a firm's AI register (#153).
v0.1.2
Breaking for scripts written against 0.1.1:
reconcile-sourceandreconcile_account_tableagree a target within half a cent
by default (--abs-tolerance 0.005), and the relative--toleranceis off unless
set. The old 1% default passed 3,000 cross-mapped between two revenue targets.
Whole-dollar controls need--abs-tolerance 0.5.EntityConfig.tax_rateis required; the old 21% default was the US federal rate.
start_monthmust beYYYY-MM, and date text given to payroll, GST, rate lookups and
format_au_datemust be ISO: day-first text such as1/10/2026is refused instead
of being read as January.- Config and 13-week cash files refuse unknown keys (
EntityConfigand its parts,
Cash13Config,WeeklyFlow), and opex amounts cannot be negative. read_xero_reportrefuses a report whose amount columns are not periods, such as a
comparison or tracking export, unlesstracking_comparison=Trueis set for a
standard P&L with one tracking option per column. It also refuses aTotal Xrow
with noXheading above it and, in the flat layout, a row with more fields than
the header, a filled row with no account, and unnamed or repeated columns.
pyfpa.io.xero_au.from_xerois removed.- Payroll tax and super guarantee lookups after 30 June 2027, the date those tables
were verified to, raise until the tables are verified again. fetch_abs_seriesreads its key only fromABS_API_KEYand no longer takes an
api_keyargument. Its keys map to the ABS Indicator API's_Hdataflows, a
response in the wrong frequency is refused, and retail trade is history only: the
ABS ceased it with the June 2025 period.- Cross-client promotion is default-deny (below),
seed_from_libraryrequires
company_rootandseeded_at, andpromote_challengerrequiresobjectiveand
approved_at. verify_workbookrequires every expected column, a matching period header and zero
check_*rows.recover_actualsraises for a line it cannot recover instead of
skipping it,score_forecastskips zero-weight lines and refuses all-zero weights,
andpyfpa.memory.diagnostics.validate_workspaceis removed in favour of
Workspace.validate.
Figures that change:
- A BAS due date on a weekend moves to the following Monday, and a leading partial
quarter settles the months the series holds instead of being dropped. - Workers compensation includes bonuses, as every state's wage definition does.
fy_summaryorders periods by date, and a divestiture keeps cogs consistent with
gross profit.
New:
- 13-week cash scenarios:
Scenario,apply_scenario,compare_scenariosand
load_cash13_scenarios. pyfpa.au.depreciationreads accounting-depreciation evidence files, refuses one
whose producer rejected it or whose digest no longer matches, and spreads a charge
in whole cents with expense and asset purchases kept apart.verify_structurechecks a workbook's layout contract, andmodel_to_excelwrites
check_*self-check rows.
Cross-client promotion:
- Cross-client promotion in
pyfpa/portfoliois default-deny.promote_priorand
promote_skillrefuse unless a practitioner has recorded aPromotionApprovalfor
that exact candidate's sha256 digest, listing the purpose, the contributing
workspaces, the authorisation reference and a confidentiality review.
record_promotion_approvalis the only writer of an approval and refuses to
overwrite one. The approval is an acknowledgement, not authentication and not legal
proof of client consent. validate_prioraccepts the candidate it validates and stamps its digest into
ValidationResult; a result without the candidate's digest cannot support a
promotion.screen_candidatereports ABN and TFN shaped numbers, email addresses,
Australian phone numbers, BSB and account patterns, narrative dollar amounts and the
contributing clients' business names, and any finding blocks promotion until the
recorded review lists it.promote_skillcopies only the files the approval lists inallowed_files. The gate
counts distinct clients by normalised business-profile heading, ignoring case,
punctuation, bracketed qualifiers and entity suffixes, and treats a workspace with no
heading as establishing no client. Promoting across two workspaces that normalise to
one name takes both workspace ids in the approval'saliases_acknowledgedand review
notes saying why they are separate.- Shared library records now carry opaque workspace ids and the approval digest instead
of client paths, with the path map in<library>/provenance/workspaces.yaml.
seed_from_librarytakes the receivingcompany_rootandseeded_at, records the
seed in that workspace's.fpa/library-seeds.yamland in the library's seed index,
and the newwithdraw_priorremoves a prior and reports the workspaces it seeded
without touching anything inside them. - Approval files store contributing workspaces as opaque ids, so nothing in a shared
library names a client directory. Building one still accepts paths. The library's seed
index records the receiving workspace by id too, andwithdraw_priorresolves ids back
to paths throughprovenance/workspaces.yamlwhen it reports affected workspaces. record_promotion_approvalwrites with an exclusive create through the new
pyfpa.io.loaders.create_yaml, so a second writer racing on the same digest is
refused instead of replacing a recorded decision.validate_priorstamps each result it produces, andpromote_priorrefuses a result
that stamp does not cover, which rejects aValidationResultbuilt by hand, edited on
disk or carried over from another candidate. The stamp is a per-process structura...
v0.2.0
v0.2.0
Breaking for code and configurations written against 0.1.2:
- Requires Python 3.12 or later, with pandas 3.0.5, PyYAML 6.0.3 and openpyxl 3.1.5 as the dependency floors (#159). 0.1.2 remains the last release for Python 3.11.
- The days-based working-capital model refuses a configuration that implies negative customer receipts, purchases or supplier payments, naming the month, the flow and the amount, and
model-exportwrites no workbook for it. A DSO of 60 days with revenue tripling in a month used to report a smaller operating cash flow instead (#139). Use a collection profile or the replenishment basis below where the days model cannot follow the business. - Forecast configuration rejects NaN and infinity (#135), a receipt shift rejects non-finite amounts and keeps cash unchanged when its source and destination month match (#136), and seasonality that overflows its total or growth that overflows raises
ValueErrorinstead of erasing revenue or producing infinite cash (#137). - Removed public names nothing read:
GstAssumptions.agent_lodgment(a YAMLagent_lodgmentkey is now ignored),XeroReport.gst_inclusive,XeroReport.unmapped_trackingand theconnector-validate --timeoutflag (#134).
Added:
review_forecaststructural controls over a monthly forecast frame (#140), an opt-in income-year tax provision (#141), receivables collected by a stated profile (#142) with bad debts and later write-offs (#150), and an opt-in replenishment basis for inventory (#151).pyfpa.au.payroll_settlementsettles payroll costs on their own dates (#143), with due-date rules for PAYG withholding (#144), the super guarantee under Payday Super and the quarterly regime (#145) and monthly payroll tax lodgers (#146), and PAYG instalments for quarterly payers (#149).- Ridgeline cash decisions with a 12-cell receipt-delay and hiring grid (#166), and a Lumbridge close handoff that reconstructs the opening payable from its own fabricated bills, credits and settlements (#170).
Fixed:
model-exportrefuses to write over its own configuration, including through links (#154); correction slugs cannot leave the corrections directory (#155); experiment saves no longer overwrite a competing save of the same name (#156); depreciation evidence refuses a non-booleansynthetic_input(#157); registry loaders report broken paths and non-mapping YAML instead of returning empty records (#158).- The Lumbridge briefing's figures are tied to their sources by tests (#138), the learning workflows keep Xero API data out until the developer terms are checked (#148), and
docs/ai-register-entry.mdgives supplier information for a firm's AI register (#153).
v0.1.2
Breaking for scripts written against 0.1.1:
reconcile-sourceandreconcile_account_tableagree a target within half a cent
by default (--abs-tolerance 0.005), and the relative--toleranceis off unless
set. The old 1% default passed 3,000 cross-mapped between two revenue targets.
Whole-dollar controls need--abs-tolerance 0.5.EntityConfig.tax_rateis required; the old 21% default was the US federal rate.
start_monthmust beYYYY-MM, and date text given to payroll, GST, rate lookups and
format_au_datemust be ISO: day-first text such as1/10/2026is refused instead
of being read as January.- Config and 13-week cash files refuse unknown keys (
EntityConfigand its parts,
Cash13Config,WeeklyFlow), and opex amounts cannot be negative. read_xero_reportrefuses a report whose amount columns are not periods, such as a
comparison or tracking export, unlesstracking_comparison=Trueis set for a
standard P&L with one tracking option per column. It also refuses aTotal Xrow
with noXheading above it and, in the flat layout, a row with more fields than
the header, a filled row with no account, and unnamed or repeated columns.
pyfpa.io.xero_au.from_xerois removed.- Payroll tax and super guarantee lookups after 30 June 2027, the date those tables
were verified to, raise until the tables are verified again. fetch_abs_seriesreads its key only fromABS_API_KEYand no longer takes an
api_keyargument. Its keys map to the ABS Indicator API's_Hdataflows, a
response in the wrong frequency is refused, and retail trade is history only: the
ABS ceased it with the June 2025 period.- Cross-client promotion is default-deny (below),
seed_from_libraryrequires
company_rootandseeded_at, andpromote_challengerrequiresobjectiveand
approved_at. verify_workbookrequires every expected column, a matching period header and zero
check_*rows.recover_actualsraises for a line it cannot recover instead of
skipping it,score_forecastskips zero-weight lines and refuses all-zero weights,
andpyfpa.memory.diagnostics.validate_workspaceis removed in favour of
Workspace.validate.
Figures that change:
- A BAS due date on a weekend moves to the following Monday, and a leading partial
quarter settles the months the series holds instead of being dropped. - Workers compensation includes bonuses, as every state's wage definition does.
fy_summaryorders periods by date, and a divestiture keeps cogs consistent with
gross profit.
New:
- 13-week cash scenarios:
Scenario,apply_scenario,compare_scenariosand
load_cash13_scenarios. pyfpa.au.depreciationreads accounting-depreciation evidence files, refuses one
whose producer rejected it or whose digest no longer matches, and spreads a charge
in whole cents with expense and asset purchases kept apart.verify_structurechecks a workbook's layout contract, andmodel_to_excelwrites
check_*self-check rows.
Cross-client promotion:
- Cross-client promotion in
pyfpa/portfoliois default-deny.promote_priorand
promote_skillrefuse unless a practitioner has recorded aPromotionApprovalfor
that exact candidate's sha256 digest, listing the purpose, the contributing
workspaces, the authorisation reference and a confidentiality review.
record_promotion_approvalis the only writer of an approval and refuses to
overwrite one. The approval is an acknowledgement, not authentication and not legal
proof of client consent. validate_prioraccepts the candidate it validates and stamps its digest into
ValidationResult; a result without the candidate's digest cannot support a
promotion.screen_candidatereports ABN and TFN shaped numbers, email addresses,
Australian phone numbers, BSB and account patterns, narrative dollar amounts and the
contributing clients' business names, and any finding blocks promotion until the
recorded review lists it.promote_skillcopies only the files the approval lists inallowed_files. The gate
counts distinct clients by normalised business-profile heading, ignoring case,
punctuation, bracketed qualifiers and entity suffixes, and treats a workspace with no
heading as establishing no client. Promoting across two workspaces that normalise to
one name takes both workspace ids in the approval'saliases_acknowledgedand review
notes saying why they are separate.- Shared library records now carry opaque workspace ids and the approval digest instead
of client paths, with the path map in<library>/provenance/workspaces.yaml.
seed_from_librarytakes the receivingcompany_rootandseeded_at, records the
seed in that workspace's.fpa/library-seeds.yamland in the library's seed index,
and the newwithdraw_priorremoves a prior and reports the workspaces it seeded
without touching anything inside them. - Approval files store contributing workspaces as opaque ids, so nothing in a shared
library names a client directory. Building one still accepts paths. The library's seed
index records the receiving workspace by id too, andwithdraw_priorresolves ids back
to paths throughprovenance/workspaces.yamlwhen it reports affected workspaces. record_promotion_approvalwrites with an exclusive create through the new
pyfpa.io.loaders.create_yaml, so a second writer racing on the same digest is
refused instead of replacing a recorded decision.validate_priorstamps each result it produces, andpromote_priorrefuses a result
that stamp does not cover, which rejects aValidationResultbuilt by hand, edited on
disk or carried over from another candidate. The stamp is a per-process structural
barrier, not a security control, so validate and promote in one session.validate_prioralso refuses a candidate whose business type or value are not the ones
its folds tested.promote_skillreads the skill tree once and writes those exact bytes, instead of
copying the client's directory again after the screen, and refuses a symlink, junction
or other reparse point inside the tree.seed_from_librarydigests each stored prior again from its driver, business type,
value and contributing workspace ids, and refuses it unless that reproduces the
recorded digest and its approval is on disk. A legacy entry with no digest, and an
entry edited after promotion, are both refused.- Loading an approval checks that the record's own
candidate_digestis the one it was
looked up by, so a record copied onto another candidate's filename is refused, and the
findings stamp writes to the digest the gate verified. - Documentation states that "nothing leaves your machine" is about network egress and
does not by itself permit moving one client's information into another client's work.
v0.1.1
- Publishes the attested wheel and source distribution to PyPI as
au-fpa-packthrough trusted publishing. - No functional change since v0.1.0.
v0.1.0
First release of the Australian FP&A pack for openfpa: 30 June financial years,
Xero AU mapping, GST and BAS cash timing, and payroll on-cost assumptions.
openfpa, a JSON-emitting CLI for company workspace intake, source lineage,
mappings,...
v0.1.2
v0.1.2
Breaking for scripts written against 0.1.1:
reconcile-sourceandreconcile_account_tableagree a target within half a cent
by default (--abs-tolerance 0.005), and the relative--toleranceis off unless
set. The old 1% default passed 3,000 cross-mapped between two revenue targets.
Whole-dollar controls need--abs-tolerance 0.5.EntityConfig.tax_rateis required; the old 21% default was the US federal rate.
start_monthmust beYYYY-MM, and date text given to payroll, GST, rate lookups and
format_au_datemust be ISO: day-first text such as1/10/2026is refused instead
of being read as January.- Config and 13-week cash files refuse unknown keys (
EntityConfigand its parts,
Cash13Config,WeeklyFlow), and opex amounts cannot be negative. read_xero_reportrefuses a report whose amount columns are not periods, such as a
comparison or tracking export, unlesstracking_comparison=Trueis set for a
standard P&L with one tracking option per column. It also refuses aTotal Xrow
with noXheading above it and, in the flat layout, a row with more fields than
the header, a filled row with no account, and unnamed or repeated columns.
pyfpa.io.xero_au.from_xerois removed.- Payroll tax and super guarantee lookups after 30 June 2027, the date those tables
were verified to, raise until the tables are verified again. fetch_abs_seriesreads its key only fromABS_API_KEYand no longer takes an
api_keyargument. Its keys map to the ABS Indicator API's_Hdataflows, a
response in the wrong frequency is refused, and retail trade is history only: the
ABS ceased it with the June 2025 period.- Cross-client promotion is default-deny (below),
seed_from_libraryrequires
company_rootandseeded_at, andpromote_challengerrequiresobjectiveand
approved_at. verify_workbookrequires every expected column, a matching period header and zero
check_*rows.recover_actualsraises for a line it cannot recover instead of
skipping it,score_forecastskips zero-weight lines and refuses all-zero weights,
andpyfpa.memory.diagnostics.validate_workspaceis removed in favour of
Workspace.validate.
Figures that change:
- A BAS due date on a weekend moves to the following Monday, and a leading partial
quarter settles the months the series holds instead of being dropped. - Workers compensation includes bonuses, as every state's wage definition does.
fy_summaryorders periods by date, and a divestiture keeps cogs consistent with
gross profit.
New:
- 13-week cash scenarios:
Scenario,apply_scenario,compare_scenariosand
load_cash13_scenarios. pyfpa.au.depreciationreads accounting-depreciation evidence files, refuses one
whose producer rejected it or whose digest no longer matches, and spreads a charge
in whole cents with expense and asset purchases kept apart.verify_structurechecks a workbook's layout contract, andmodel_to_excelwrites
check_*self-check rows.
Cross-client promotion:
- Cross-client promotion in
pyfpa/portfoliois default-deny.promote_priorand
promote_skillrefuse unless a practitioner has recorded aPromotionApprovalfor
that exact candidate's sha256 digest, listing the purpose, the contributing
workspaces, the authorisation reference and a confidentiality review.
record_promotion_approvalis the only writer of an approval and refuses to
overwrite one. The approval is an acknowledgement, not authentication and not legal
proof of client consent. validate_prioraccepts the candidate it validates and stamps its digest into
ValidationResult; a result without the candidate's digest cannot support a
promotion.screen_candidatereports ABN and TFN shaped numbers, email addresses,
Australian phone numbers, BSB and account patterns, narrative dollar amounts and the
contributing clients' business names, and any finding blocks promotion until the
recorded review lists it.promote_skillcopies only the files the approval lists inallowed_files. The gate
counts distinct clients by normalised business-profile heading, ignoring case,
punctuation, bracketed qualifiers and entity suffixes, and treats a workspace with no
heading as establishing no client. Promoting across two workspaces that normalise to
one name takes both workspace ids in the approval'saliases_acknowledgedand review
notes saying why they are separate.- Shared library records now carry opaque workspace ids and the approval digest instead
of client paths, with the path map in<library>/provenance/workspaces.yaml.
seed_from_librarytakes the receivingcompany_rootandseeded_at, records the
seed in that workspace's.fpa/library-seeds.yamland in the library's seed index,
and the newwithdraw_priorremoves a prior and reports the workspaces it seeded
without touching anything inside them. - Approval files store contributing workspaces as opaque ids, so nothing in a shared
library names a client directory. Building one still accepts paths. The library's seed
index records the receiving workspace by id too, andwithdraw_priorresolves ids back
to paths throughprovenance/workspaces.yamlwhen it reports affected workspaces. record_promotion_approvalwrites with an exclusive create through the new
pyfpa.io.loaders.create_yaml, so a second writer racing on the same digest is
refused instead of replacing a recorded decision.validate_priorstamps each result it produces, andpromote_priorrefuses a result
that stamp does not cover, which rejects aValidationResultbuilt by hand, edited on
disk or carried over from another candidate. The stamp is a per-process structural
barrier, not a security control, so validate and promote in one session.validate_prioralso refuses a candidate whose business type or value are not the ones
its folds tested.promote_skillreads the skill tree once and writes those exact bytes, instead of
copying the client's directory again after the screen, and refuses a symlink, junction
or other reparse point inside the tree.seed_from_librarydigests each stored prior again from its driver, business type,
value and contributing workspace ids, and refuses it unless that reproduces the
recorded digest and its approval is on disk. A legacy entry with no digest, and an
entry edited after promotion, are both refused.- Loading an approval checks that the record's own
candidate_digestis the one it was
looked up by, so a record copied onto another candidate's filename is refused, and the
findings stamp writes to the digest the gate verified. - Documentation states that "nothing leaves your machine" is about network egress and
does not by itself permit moving one client's information into another client's work.
v0.1.1
- Publishes the attested wheel and source distribution to PyPI as
au-fpa-packthrough trusted publishing. - No functional change since v0.1.0.
v0.1.0
First release of the Australian FP&A pack for openfpa: 30 June financial years,
Xero AU mapping, GST and BAS cash timing, and payroll on-cost assumptions.
openfpa, a JSON-emitting CLI for company workspace intake, source lineage,
mappings, reconciliation, connector scaffolding, corrections, scorecards,
context packs and verified Excel export.- 17 skills under
skills/, plus the.claude-plugin/plugin.jsonmanifest that
declares the repository as theopenfpaplugin. - Five worked examples: Lumbridge Services, Harbour Light and Ridgeline Chair
Co. from synthetic records, ARB Corporation from its FY2025 Appendix 4E, and
Fox Factory Holding Corp. from public SEC filings. - The distribution is
au-fpa-packand the import ispyfpa. Version 0.1.1
is available on PyPI. - Tested on Python 3.11, 3.12 and 3.13.
v0.1.1
v0.1.1
- Publishes the attested wheel and source distribution to PyPI as
au-fpa-packthrough trusted publishing. - No functional change since v0.1.0.
v0.1.0
First release of the Australian FP&A pack for openfpa: 30 June financial years,
Xero AU mapping, GST and BAS cash timing, and payroll on-cost assumptions.
openfpa, a JSON-emitting CLI for company workspace intake, source lineage,
mappings, reconciliation, connector scaffolding, corrections, scorecards,
context packs and verified Excel export.- 17 skills under
skills/, plus the.claude-plugin/plugin.jsonmanifest that
declares the repository as theopenfpaplugin. - Five worked examples: Lumbridge Services, Harbour Light and Ridgeline Chair
Co. from synthetic records, ARB Corporation from its FY2025 Appendix 4E, and
Fox Factory Holding Corp. from public SEC filings. - Source-only. The distribution is
au-fpa-packand the import ispyfpa; it
is not published to PyPI. - Tested on Python 3.11, 3.12 and 3.13.
v0.1.0
v0.1.0
First release of the Australian FP&A pack for openfpa: 30 June financial years,
Xero AU mapping, GST and BAS cash timing, and payroll on-cost assumptions.
openfpa, a JSON-emitting CLI for company workspace intake, source lineage,
mappings, reconciliation, connector scaffolding, corrections, scorecards,
context packs and verified Excel export.- 17 skills under
skills/, plus the.claude-plugin/plugin.jsonmanifest that
declares the repository as theopenfpaplugin. - Five worked examples: Lumbridge Services, Harbour Light and Ridgeline Chair
Co. from synthetic records, ARB Corporation from its FY2025 Appendix 4E, and
Fox Factory Holding Corp. from public SEC filings. - Source-only. The distribution is
au-fpa-packand the import ispyfpa; it
is not published to PyPI. - Tested on Python 3.11, 3.12 and 3.13.