Bundle
dsh-dd-forge
Due-diligence document review for DeepSeek Harness: opcore deterministic engine (documents -> rule packs -> risk report) plus review/report-writing agent skills. Rules are data (YAML), not code.
- Source
- McBonB
- License
- MIT
- Updated
- Updated 5 days ago
Readme
# dsh-dd-forge — due-diligence document review (DeepSeek Harness plugin)
English | [中文](./README.zh.md)
**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)](https://github.com/DeepSeek-AI/deepseek-harness); 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
```bash
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 (the `packs` parameter optionally narrows to a pack subset); returns findings and writes the report into the workspace (`reportPath` overrides the report location); the `format` parameter selects `md` / `docx` / `xlsx` (comma-separated, default `md`); the top-up loop parameters are `saveRun` (serialize this run into a re-runnable review.json snapshot) and `baseline` (diff against a previous snapshot — see "Top-up re-run loop"); pass a `targets` array 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:
```yaml
- 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 a `sheet!A6` locator; excerpts are verbatim, capped at 80 chars.
- On a rule hit, the fact paths referenced by its `when`/`assert` are traced back through provenance; derived values (ratios, reconciliation gaps) map onto their input rows via `DERIVED_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_data` outcomes list **which inputs are missing** (labelled with the document class they should arrive in) in the report's data-gap table.
- CLI `--json` and the `dd_review` tool return `evidence` on every finding and `missingInputs` on every missing-data rule.
Rendered in the report as:
```markdown
- **[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** (`docx` npm 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).
```bash
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:
```bash
# 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@case` without 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_review` parameters are `saveRun` (write the snapshot) and `baseline` (diff and render); the return value carries `comparison`.
## Standalone (without dsh)
```bash
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
- [x] evidence chain: excerpt/file/line citations per finding
- [x] industry packs: bulk trade (`trade-bulk`, 10 rules), logistics (`logistics`, 8 rules), labour (`labour`, 8 rules)
- [x] delivery layer: docx report and xlsx risk register (`--format md,docx,xlsx`)
- [x] top-up re-run loop: two-round diff (`--save-run` snapshot, `diff` three-way classification with missing-data closure rate, 「与上轮对比」 report section)
- [x] deeper dsh integration: `dd_review` multi-folder batch (`targets` array: one report per folder + the `dd-review-index.md` summary 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.
Install
dsh plugin --profile web add github:McBonB/dsh-dd-forge
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-dd-forge from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.