dsh-dd-forge — due-diligence document review (DeepSeek Harness plugin)
English | 中文
Documents → rule packs → risk report: feed the deterministic engine a due-diligence folder (registry / equity / financials / contracts / charters / employment contracts) and get a sourced risk report. Built for DeepSeek Harness (dsh); the engine (opcore) itself has zero dsh dependencies and runs standalone.
┌──────────────────────────────────────────────────┐
│ dsh session (model + judgement) │
│ skills: dsh-dd-forge-review / -report │ ← judgement layer (prompt skills)
│ tools: dd_review / dd_rules │ ← deterministic layer (registered tools)
├──────────────────────────────────────────────────┤
│ opcore engine (engine/, zero dsh deps) │
│ loader (docs → facts) → rule execution → report │
├──────────────────────────────────────────────────┤
│ packs/ (YAML rule packs — data, not code) │
│ general-company 12 · contract-basics 8 │
│ trade-contract 8 · trade-bulk 10 │
│ logistics 8 · labour 8 · credit-financials 8 │
│ guarantee 5 · corporate-charter 5 │
└──────────────────────────────────────────────────┘
Install
dsh plugin --profile <name> add dsh-dd-forge
Local development: dsh plugin --profile <name> add /path/to/dsh-dd-forge — pnpm link: installs do not auto-install dependencies, so run npm install in this directory first.
Use
In a dsh session, hand a target folder to the model ("run a due-diligence review on ./target-company"), or call the tools directly:
dd_review— run all rule packs over a folder (thepacksparameter optionally narrows to a pack subset); returns findings and writes the report into the workspace (reportPathoverrides the report location); theformatparameter selectsmd/docx/xlsx(comma-separated, defaultmd); the top-up loop parameters aresaveRun(serialize this run into a re-runnable review.json snapshot) andbaseline(diff against a previous snapshot — see "Top-up re-run loop"); pass atargetsarray to review several folders in one call (see "Batch review")dd_rules— list packs and every rule with severity, category, and public reference
Skills: dsh-dd-forge-review (folder conventions + verification discipline for high findings), dsh-dd-forge-report (writing the full DD report on top of engine output).
Batch review (several folders)
To review several subjects in one session, dd_review takes a targets array (mutually exclusive with target): targets=["./companyA","./companyB"]. Each folder gets its own report set (exported per format as <workspace>/dd-review-report-<company>.<ext>; the folder name when no registry document names the subject; duplicates get -2/-3 suffixes), plus one summary index at <workspace>/dd-review-index.md (indexPath overrides the location): a per-subject conclusion/stats table, a cross-subject high-severity quick view, totals, and relative links to every report — closed by the same disclaimer.
Every folder is validated up front (one inaccessible folder fails the whole call — no half delivery). reportPath / saveRun / baseline are single-run parameters and are rejected with targets — to take one folder through the top-up loop, call the tool with target for it alone.
Folder conventions
The engine recognizes documents by filename patterns; anything missing simply leaves the corresponding rules as missing_data:
| Document | File | Format |
|---|---|---|
| Registry | 工商信息.json (or any json matching 工商/registry) |
Chinese or English keys; capital in 万元 |
| Equity | *股权*.csv or .xlsx (filename/sheet matching 股权/股东) |
header: 股东,认缴出资(万元),实缴出资(万元),持股比例(%),备注 |
| Financials | *资产负债*.csv, *利润*.csv, *现金流量*.csv (or same-named xlsx sheets) |
accounts as rows, years as columns (科目,2024,2023), 万元 |
| Contracts | contracts/*.md / .txt / .docx |
one contract per file; optional frontmatter: 标题, 签订日期 |
| Charter | any 章程-matching .md / .txt / .docx |
extracts company name, registered capital, shareholder rows, mandatory items, share-transfer clause |
| Employment contract | any .md / .txt / .docx whose filename contains 「劳动合同」 or whose body contains 「劳动合同期限」 |
extracts contract term, probation months and its statutory tiered cap, probation/agreed salary, non-compete term and compensation, service period with training cost and penalty, mandatory items and rules-policy wording |
docx (contracts/charters) and xlsx (financials/equity) are parsed natively (mammoth/exceljs, lazily loaded — zero cost when absent). Only scans/images need in-session conversion first.
The loader also extracts clause facts from contract bodies for the specialized packs: deposit amount and ratio ("定金…N 元"), advance amount and ratio (amount form "预付款 N 元" or percent form "预付 N%", feeding the bulk-trade pack), payment term days ("N 日内付清/支付" — deadlines that pay a deposit/advance do not count; read by both the trade and logistics packs), daily penalty rate ("万分之X/日", auto-annualized), trade-contract classification (购销/买卖/采购/销售/供货/大宗商品/贸易 — bulk-trade rules report 不适用 on non-trade contracts), logistics-contract classification (货物运输/运输服务/承运/仓储/配送 — logistics rules report 不适用 on non-logistics contracts; a counterparty named 「物流」 does not classify a contract), plus 36 clause-presence probes (risk transfer, force majeure, notice, acceptance, exclusivity, third-party inspection, price adjustment/点价, deposit-vs-penalty election, documents of title, auto-renewal, performance bond, settlement & invoicing, foreign-related & dispute institution, cargo damage liability, insured/declared-value carriage, loading duties, subcontracting restrictions, POD & reconciliation, cargo claims, vehicle & driver qualifications, …).
Employment contracts are parsed as their own document type (they never enter the commercial-contract candidate set): contract term in years (date form 「劳动合同期限自 X 起至 Y 止」 first, stated years as fallback; open-ended contracts detected separately), probation months with the statutory tiered cap (Labour Contract Law art. 19 tiers: <3 months 0 / 3 months-1 year 1 / 1-3 years 2 / ≥3 years & open-ended 6), probation vs agreed salary (auto-derived 80%-line ratio), non-compete term (months/years normalized) and compensation clause (an explicit 「无需支付经济补偿」 waiver does not count as agreed), service period with training cost and penalty, plus art.-17 mandatory-item and rules-policy probes.
Rule packs
Nine packs, 72 rules, each with a public reference (statute or an explicitly labelled convention threshold):
| Pack | Rules | Coverage |
|---|---|---|
general-company |
12 | registry status, paid-in ratio, equity cross-checks, balance-sheet identity & solvency, guarantee resolutions |
contract-basics |
8 | essential clauses, digit-vs-uppercase amounts, signing date and signature block |
trade-contract |
8 | deposit cap (Civ. Code art. 586, 20%), payment term (90-day convention), risk transfer, force majeure, daily penalty annualized |
trade-bulk |
10 | bulk-trade special: advance ratio (30% convention), third-party inspection, price adjustment/点价, deposit-plus-penalty election (Civ. Code art. 588), force majeure excluding market swings, documents of title (仓单/提单), auto-renewal exit, performance bond, settlement & invoicing, foreign-related dispute institution |
logistics |
8 | logistics special: cargo damage/loss liability (Civ. Code art. 832), insured/declared-value carriage, freight payment term (60-day convention reading the same fact as TC-002's 90-day line — layered thresholds), loading duties, subcontracting restrictions (cf. art. 923), POD & reconciliation, cargo-claim procedure & deadline, vehicle & driver qualifications |
labour |
8 | employment special: mandatory clauses (Labour Contract Law art. 17), probation tiered cap and 80% salary line (arts. 19/20), non-compete compensation and two-year cap (arts. 23/24), service-period penalty vs training cost (art. 22), social insurance (Social Insurance Law art. 58), rules-policy basis (art. 4); applies only to employment-contract documents |
guarantee |
5 | guarantee type, guarantee period, mortgage registration, scope, counter-guarantee |
credit-financials |
8 | current/quick ratio, interest-bearing debt, negative operating cash flow, earnings-vs-cash divergence, cashflow identity, asset quality |
corporate-charter |
5 | charter-vs-registry cross-checks (name/capital/shareholder list), Company Law art. 46 mandatory items, share transfer |
Rule DSL
Rules are YAML data, compiled and validated at load; they never execute code:
- id: FIN-001
name: Balance-sheet identity violated
severity: high # high | medium | low
category: 财务勾稽
target: case # case = once per case; contract/charter/labour = once per document (contract.*/charter.*/labour.* bound)
when: financials.balanceSheetGap != null
assert: financials.balanceSheetGap <= 0.01
message: 'gap {financials.balanceSheetGap} 万元'
reference: 企业会计准则——基本准则 (accounting identity)
remediation: re-check data entry, then request the official statements
The expression language is a safe subset (numbers/strings/booleans/null, dotted paths, + - * /, == != > < >= <=, and or not, parentheses) evaluated by a recursive-descent parser — no eval. Null semantics: equality may compare against null (x != null guards); every other operation propagates null, and assert: null is reported as missing_data, never a pass — absence of evidence is not evidence of absence.
Outcomes: finding / pass / missing_data / inapplicable.
Evidence chain (provenance)
Every finding carries 0-3 source citations, turning the report from conclusions into a checkable chain of evidence:
- While extracting each key fact the loader records its source via
note()(CaseData.provenance: factPath →{file, line, excerpt}). Text facts cite the physical line number (docx has none — mammoth emits one line per paragraph, so the line number is the paragraph number); csv rows resolve back to the line in the raw file; xlsx cites asheet!A6locator; excerpts are verbatim, capped at 80 chars. - On a rule hit, the fact paths referenced by its
when/assertare traced back through provenance; derived values (ratios, reconciliation gaps) map onto their input rows viaDERIVED_INPUTS(e.g. CF-001 current ratio → the 「流动资产合计」 and 「流动负债合计」 rows). This works for any rule pack — no per-rule wiring. - Clause-absence findings (the text to quote does not exist) fall back to a file-level citation; contract/charter rules are bound to their file anyway.
missing_dataoutcomes list which inputs are missing (labelled with the document class they should arrive in) in the report's data-gap table.- CLI
--jsonand thedd_reviewtool returnevidenceon every finding andmissingInputson every missing-data rule.
Rendered in the report as:
- **[CF-001] 流动比率低于 1**(流动性)
- 发现:流动比率 0.81(流动资产/流动负债,提示线 1)
- 证据:
> 财务/资产负债表.csv L3:「流动资产合计,1330,1180」
> 财务/资产负债表.csv L8:「流动负债合计,1650,1420」
Delivery formats (md / docx / xlsx)
One review exports to three formats sharing one report number and one disclaimer (number = OPC-<date>-<checksum>; the same inputs re-exported the same day keep the number, so the files of one delivery cite each other):
- md (default) — the session-friendly anchor format shown above
- docx (
docxnpm package, MIT, pure JS) — delivery Word document: cover (company / report number / date / engine version / disclaimer summary), table of contents (a static outline — no field-update prompt in Word/WPS/Pages), overall conclusion, risk summary table (severity / id / risk / location / reference), per-finding detail with evidence quotes, passes & data gaps, and a signature block (审查 / 复核 / 签发日期, left blank) closed by the full disclaimer - xlsx (exceljs) — three-sheet risk register:
风险明细(one row per finding: id / severity / risk / location / message / evidence / remediation / reference, severity colouring, report-number + disclaimer footer),数据缺失清单(rules awaiting inputs and exactly which inputs; an explicit none-row when clean),规则清单(every loaded rule with its public reference — what was checked, against what)
CLI: --format md,docx,xlsx (comma-separated multi-select; -o names the report base, each format gets its extension; without -o the default is dd-review-report.<ext>). The dd_review tool takes a format parameter that passes through to the engine exporters (report paths swap extensions per format; the tool returns a reportPaths array).
node engine/cli.js review fixtures/demo-company -o report --format md,docx,xlsx
# → report.md / report.docx / report.xlsx
npm test includes roundtrip checks: mammoth reads the generated docx back and asserts cover/TOC/summary-table/evidence/signature-block content; exceljs reads the xlsx back and asserts the three sheets and row counts (45 findings, 72 rules, the data-gap list).
Top-up re-run loop (two-round diff)
Due diligence is multi-round. The engine serializes each run into a re-runnable snapshot and diffs the re-run against it:
# round 1: review and save the run snapshot (findings / missingData / stats)
node engine/cli.js review <folder> --save-run review.json
# …client tops up documents per the report's data-gap table (keep file names stable)…
# round 2: re-run the current folder against the old snapshot
node engine/cli.js diff review.json <folder> # three-way classification + closure rate
node engine/cli.js diff review.json <folder> --json # machine-readable
node engine/cli.js diff review.json <folder> -o report.md # report embeds the 与上轮对比 section
node engine/cli.js review <folder> --baseline review.json # or: review takes the baseline directly
- Matching key: a finding is
ruleId@file(ruleId@casewithout a document scope); re-rendered numbers do not change its classification. A renamed document shows up as resolved+新增, not as a match. - Three-way classification: 消除 (old run, gone — resolved or withdrawn) / 新增 (new this round — verify each one) / 残留 (both rounds — escalate or explicitly accept).
- Missing-data closure rate = previous gaps decidable this round / previous gaps. A closed gap may become a pass or a finding (the data arrived and tripped the rule) — the latter also appears under 新增.
- Reports: with a baseline, md/docx render a 「与上轮对比(补料复跑)」 subsection under the overall conclusion (previous/current stats, the classification table, closure rate) and tag new findings 「— 本轮新增」 in the detail walk; xlsx appends a fourth sheet 与上轮对比 (row-per resolved/added, remaining aggregate, gap list, closure-rate line). Without a baseline all three formats are unchanged.
- Tool: the
dd_reviewparameters aresaveRun(write the snapshot) andbaseline(diff and render); the return value carriescomparison.
Standalone (without dsh)
node engine/cli.js review <folder> [-o report.md] [--json] [--format md,docx,xlsx] [--save-run review.json] [--baseline review.json]
node engine/cli.js diff <old-snapshot.json> [folder] [--json] [-o report.md] [--save-run review.json] [--format md,docx,xlsx]
node engine/cli.js rules
node engine/cli.js review <folder> --packs <pack-dir>
Batching several folders (targets) is a dsh tool-side capability; with the CLI, loop review over the folders.
Engine API: import { runReview, loadPacks, renderReport, renderBatchIndex, renderDocxReport, renderXlsxReport, snapshotFromResult, readSnapshot, diffRuns } from 'dsh-dd-forge/opcore' (docx/xlsx renderers load lazily — nothing is paid unless that format is exported; renderBatchIndex renders the batch summary index).
fixtures/demo-company/ is a fully synthetic fixture (fictional company, people, numbers); npm test asserts the end-to-end result against it: 45 findings (10 high / 24 medium / 11 low), 78 passes, 69 inapplicable (including bulk-trade rules on non-trade contracts, logistics rules on non-logistics contracts, and the service-period rule on an employment contract with no 服务期 agreed) — plus the evidence citations of 10 representative findings (file/line/excerpt), xlsx citation locators, the missing-input list of missing_data rules, the roundtrip content of the docx/xlsx delivery formats (see "Delivery formats"), and the top-up re-run diff on a temp copy of the fixtures (equity table restored / transport contract withdrawn / payment term extended to manufacture the deltas; asserts resolved/added/remaining and the closure rate, plus the CLI --save-run/diff/--baseline loop end-to-end — see "Top-up re-run loop"), plus the dd_review tool layer (targets batch: per-target report naming and duplicate suffixes, summary-index content and relative links, multi-format export, parameter exclusivity, and a single-target return-shape regression guard).
License layering
- Code (
engine/,src/): MIT - Sample rule packs (
packs/): CC-BY-NC-4.0 — rule packs are knowledge assets; commercial licensing is separate. This layering is the legal basis of the "open engine, paid industry packs" model.
Rules derive from public laws, regulations, and standard DD practice (each rule's reference states its source); no proprietary internal standards are included.
Roadmap
- evidence chain: excerpt/file/line citations per finding
- industry packs: bulk trade (
trade-bulk, 10 rules), logistics (logistics, 8 rules), labour (labour, 8 rules) - delivery layer: docx report and xlsx risk register (
--format md,docx,xlsx) - top-up re-run loop: two-round diff (
--save-runsnapshot,diffthree-way classification with missing-data closure rate, 「与上轮对比」 report section) - deeper dsh integration:
dd_reviewmulti-folder batch (targetsarray: one report per folder + thedd-review-index.mdsummary index) - configurable thresholds (pack-level overrides)
- standalone opcore npm package
Disclaimer
Output is deterministically generated from user-provided documents under public rules. It is information structuring and risk flagging only — not legal, audit, or investment advice.
No comments yet. Be the first to write one.