Skip to content
dsh.fish
Bundle

dsh-bid-ca-precheck

投标文件符合性预检(按招标文件逐条核对响应情况,仅提示差异,不作出定性结论)

Source
PerryLink
License
Apache-2.0
Updated
Updated 2 days ago

Readme

# dsh-bid-ca-precheck — Bid document compliance pre-check against each tender requirement

[![DSH Market](https://raw.githubusercontent.com/2BingLing/dsh-market/master/assets/readme/badge-listed-en.svg)](https://dsh.market/)

`dsh-bid-ca-precheck` reads one 投标符合性响应台账 — the tender's requirements and the bidder's responses, keyed by the register's own column names in Chinese or English — applies a versioned rule pack, and checks that register's own completeness and internal consistency: that every tender requirement has a recorded response, that the verdict comes from the vocabulary you configure, that clause numbers are unique within the register, that the register declares its project and its bidder, that a clause you marked high-risk carries evidence, and that the requirement column still holds real requirements rather than template placeholders.

## What it looks like

![Terminal demo of dsh-bid-ca-precheck: real output over its BC-001 fixture](https://raw.githubusercontent.com/PerryLink/dsh-bid-ca-precheck/main/docs/assets/dsh-bid-ca-precheck-demo.png)

Real output from this plugin over its own `BC-001` test fixture — not a mock-up. The rule pack ships no invented quotations, so a finding names both the clause it applied and the fact that the clause text was not obtained.

## What it answers

| You ask | What it answers |
|---|---|
| A tender requirement's `response` column is blank. Is that reported? | Yes. `BC-001` reports the row, because every tender requirement must have a recorded response. It only checks that the `response` column is filled — it does not judge whether the response satisfies the requirement, which is the evaluation committee's call. |
| I filled the `verdict` column and the report still says the rule could not run. Why? | `BC-002` compares each value with the verdict vocabulary you configure, and that list ships empty, so until you fill `values` the rule reports itself in `skipped` rather than guessing. Once configured it only reports a value that is not in your list; it never judges whether the verdict itself is correct. |
| The same clause number appears on two rows of the register. | `BC-003` reports the repeated `clause` value: when one number appears twice, nobody can tell which row answers which requirement. Whitespace is ignored, so `3.2` and ` 3.2 ` count as the same number. It only checks uniqueness — it does not decide which of the two rows is right; a hit usually means duplicate registration or a copied-wrong number. |
| The register does not say which project it covers or who the bidder is. | `BC-004` reports the missing header field and requires both `project` and `bidder`. That need comes from traceability — a verdict that cannot be tied to a project and a bidder cannot be reviewed later — not from a clause that requires a register header. |
| Which rows must carry evidence? | `BC-005`: every row whose `requirement` text contains one of your high-risk keywords, matched by containment. `highRiskKeywords` ships empty, so until you fill it the rule reports itself in `skipped` rather than passing silently. It only checks that the `evidence` column is filled, not whether the evidence is valid or sufficient. |
| A few rows still read 【……】 or 待填 in the requirement column. | `BC-006` reports them: `【`, `】`, `{{`, `}}`, `XXX`, `待填`, `TBD` and similar terms left in the `requirement` column usually mean the register was copied from a template and never filled in. The terms list is yours to adjust. The rule flags the placeholder only; it does not decide whether the requirement itself is real. |

## Standards it follows

| Document | Number | Cited by rules |
|---|---|---|
| 《中华人民共和国招标投标法》 | 1999年8月30日通过,2017年12月27日修正(全国人大常委会《关于修改〈中华人民共和国招标投标法〉、〈中华人民共和国计量法〉的决定》),本法自2000年1月1日起施行 | BC-001, BC-003, BC-004, BC-006 |
| 《中华人民共和国招标投标法实施条例》 | 国务院令第613号(2011 年 12 月 20 日公布,2017 年 3 月 1 日修订,自 2012 年 2 月 1 日起施行) | BC-002 |
| 《中华人民共和国招标投标法》 | 1999年8月30日通过,2017年12月27日修正,自2000年1月1日起施行 | BC-005 |

**Boundary:** this plugin checks a **投标符合性响应台账** for what a register can be held to — that every
tender requirement has a recorded response, that verdicts come from your vocabulary and clause numbers are
unique, that a high-risk clause carries evidence, and that the requirement column still holds real
requirements rather than template placeholders. It does **not** decide whether a bid is valid, whether it
should be rejected, or whether a response is a material deviation. **Those calls belong to the evaluation
committee, and this plugin never makes them** — a test asserts that no rule here can be `error`.

> ### ⚠️ Read this before trusting a citation in the report
>
> **Every `excerpt` in this plugin's rule pack says, in so many words, that the clause text was not
> obtained.** The regime lives in 《招标投标法》, 《招标投标法实施条例》 and each project's 评标办法. The
> verification pass for this plugin could not retrieve verbatim clause text from them, so rather than
> paraphrase a quotation the pack states the gap in the `excerpt` field itself and puts the honest
> reasoning in `note`. Every rule is therefore `warn` or `info`, and a test asserts that no rule claims a
> quotation it does not have. **When the texts are in hand, two things must be done: replace each
> `excerpt` with the real clause, and raise `kind` to `direct`.**
>
> The worst failure mode of a register tool is a register copied from a template and mistaken for a
> checked one, so `BC-006` looks for exactly that: `【】`, `{{}}`, `XXX`, `待填`, `TBD` left in the
> requirement column.
>
> Two lists are yours, not a standard's: the verdict vocabulary (`BC-002`) and the high-risk keywords
> (`BC-005`). Both ship **empty**, and a rule whose list is unset reports itself in `skipped` rather than
> passing silently.

## Compatibility

| Surface | Status |
|---|---|
| Harness | Peer range `>=0.1.2-rc.1 <0.2.0 \|\| >=0.2.0-0 <0.3.0` — verified to accept both `0.2.0-rc.2` and `0.2.1-alpha.1`. `engines.dsh` is deliberately not declared: it has no reader and cannot reject a host |
| Node | `^22.19.0 || >=24.0.0` |
| Platforms | All (plain ESM; no native code, no network, no model call) |
| Tool mode | Works in `native`, `ptc` and `both`; for a batch of registers use `ptc` |

## What it does

Registers the `bid_ca_precheck` tool. It reads one compliance register — the tender's requirements and the
bidder's responses, keyed by the register's own column names in Chinese or English — applies a versioned
rule pack, and returns a report.

| Rule | Check | Severity | Basis kind |
|---|---|---|---|
| `BC-001` | every requirement has a recorded response | warn | direct |
| `BC-002` | the verdict uses your vocabulary (off by default) | info | local |
| `BC-003` | clause numbers are unique in the register | warn | principle |
| `BC-004` | the register names its project and its bidder | warn | principle |
| `BC-005` | a high-risk clause carries evidence (off by default) | warn | direct |
| `BC-006` | the requirement column holds no unreplaced placeholder | warn | principle |
## Install

```sh
dsh plugin --profile <name> add dsh-bid-ca-precheck
dsh --profile <name> --dump-config | grep 'dsh-bid-ca-precheck'
```

## Configuration

| Key | Type | Default | Description |
|---|---|---|---|
| `rulesFile` | string | `rules/bid-ca-precheck.yaml` | Rule-pack path, relative to the package root |
| `disabledRules` | string[] | `[]` | Rule ids to stop running; each appears in `skipped` |
| `onlyRules` | string[] | `[]` | Run only these rule ids; empty runs every rule |
| `skipNotes` | string | `""` | Note appended to every `skipped` reason |
| `timeoutMs` | number | `120000` | Cooperative tool timeout budget |

Rule-level parameters worth knowing:

- `BC-002` `values` — your verdict vocabulary, e.g. `[满足, 部分满足, 不满足, 待确认]`. Empty means the
  rule does not run. It can also be passed per call.
- `BC-005` `highRiskKeywords` — the words that mark a clause as needing evidence, e.g.
  `[资格, 业绩, 实质性, 星号条款]`. Empty means the rule does not run.
  `evidenceField` and `requirementField` rename the two columns it reads.
- `BC-006` `terms` — the placeholders to look for; the default list covers `【】`, `{{}}`, `XXX`, `待填`,
  `待补充`, `TBD`, `todo`.

## Material format

The tool accepts JSON or YAML:

```yaml
project: 某某厂房工程
bidder: 某某建设有限公司
rows:
  - { 条款号: "2.1", 招标要求: 投标人应具有建筑工程施工总承包二级及以上资质,
      投标响应: 已提供资质证书复印件,等级为二级, 响应结论: 满足, 证明材料: 资格文件第 12 页 }
  - { Clause: "2.2", Requirement: 近三年至少完成两项同类业绩,
      Response: 已提供合同及验收证明, Verdict: 满足, Evidence: 业绩文件第 3 页 }
```

Column names are matched case-insensitively and ignoring spaces, underscores and hyphens, so `招标要求`
and `requirement` resolve to the same field; the register's own column names are kept, so a finding names
the column it actually read. A row may declare its own number via `row`, `序号` or `行号`.

## Rule sources

Rule data lives in `rules/bid-ca-precheck.yaml`. The pack's header states the citation gap in full, and
each rule's `note` repeats the part that matters for that rule. The load-time guard that normally enforces
"an excerpt must be a real quotation of at least eight characters" cannot tell a quotation from a
description — so this pack leans on the header, the per-rule notes and a test that asserts every `excerpt`
admits the gap.

## Troubleshooting

- **`BC-002` or `BC-005` report themselves as skipped.** Their lists are empty. Both depend on your
  project, not on a regulation, and the plugin will not guess them.
- **`BC-006` fires on a requirement I meant literally.** The word `XXX` or `待填` appears in it. Narrow the
  `terms` list, or fix the register.
- **The reader refuses a register it used to accept.** It found none of the known column names; the error
  names the columns it saw. Rename one column to a known alias, or add the alias to the plugin's column
  spec.
- **`BC-001` fires on rows I consider informational.** A blank response means "not checked", which is the
  one thing a pre-check register must never be. If a clause genuinely needs no response, record that in
  the register rather than leaving it blank.
- **The plugin installs but the tool never appears.** Check that `main` resolves to `lib/index.mjs` and
  that `pnpm run build` produced it; a wrong `main` makes the loader skip the entry silently.
- **`dsh plugin add` refuses the package as incompatible.** The peer range covers `0.1.x` and `0.2.x`; if
  your runtime sits outside it, grant an explicit exemption:
  `dsh plugin --profile <name> allow-version dsh-bid-ca-precheck@0.1.0 --dsh-version <runtime> --accept-risk`
- **`check` reports `manifest-peers` as failed.** The static checker compares against a hard-coded peer
  range that predates the 0.2 line. The runtime enforces peer compatibility at install time, so the
  declared range is the correct one; this is a known upstream issue in `dsh-plugin-dev`.

## Development

```sh
pnpm install
pnpm run typecheck   # tsc --noEmit
pnpm test            # vitest, paired fixtures per rule
pnpm run build       # tsdown -> lib/index.mjs + lib/index.d.mts
node ../scripts/sync-shared.mjs dsh-bid-ca-precheck   # refresh src/shared from ../_shared
```

The plugin is **data-only**: `src/model.ts` declares the table shape, the shared kit supplies the reader
and the check engine, and the rule pack declares every check. A new check that fits an existing kind needs
a rule pack edit and nothing else.

## License

[Apache License 2.0](LICENSE) © 2026 dsh-bid-ca-precheck contributors.

Install

dsh plugin --profile web add github:PerryLink/dsh-bid-ca-precheck

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source