文件
wangsli fe025e65cf
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s
fix: 优化报销制单下载与付款进度(09-08-reimbursement-payment-closeout)
2026-09-10 16:39:01 +08:00

30 KiB

Security And Privacy

Never Store In Repo / Trellis / Chat

  • Passwords, token, Secret, private keys, EncodingAESKey.
  • Full personal accounts, full phone numbers, full email addresses where not required for local config.
  • Raw invoice images, contract bodies, bank statement originals, payment credentials, sensitive quotes, cost details.
  • Complete local paths to confidential company directories.
  • Enterprise WeChat raw payloads, full sp_no, full template IDs, full userid, form values, attachments or comments.

Trellis-Specific Rules

  • .trellis/workspace/ is local-only by default.
  • .trellis/tasks/ may contain PRD and acceptance context, but only in redacted form.
  • scripts/qa/check-trellis-governance.mjs must pass before commit.

Scenario: AP Full-Ledger Reading Is Not Payment Review

1. Scope / Trigger

AP financial summary and reimbursement ledger reads, not general entity visibility.

2. Signatures

  • Permission: finance.ledger.view in both server and client role maps.
  • Reads: GET /api/ap-operations and GET /api/ap-operations/reimbursement-ledger.
  • Separate export: POST /api/exports/ap-operations.

3. Contracts

Grant ledger reading to finance, finance_mgr, not ceo, manager or system_admin. Preserve emergency admin wildcard semantics. Both reads require the dedicated permission before data queries; query filters cannot confer access. Frontend route and navigation projections use the same permission. Keep manager payment review and multi-role permission unions intact; never grant CEO payment management to preserve read access. Export keeps its existing independent payment.batch.manage / risk.review.manage rule.

4. Validation & Error Matrix

Feature enabled: missing ledger permission returns 403 / PERM_001, with no financial query. Feature disabled: the existing 409 refusal may occur first. Finance/finance manager and manager+finance or ceo+finance retain reading. CEO alone is refused. Old payment or risk permissions alone no longer imply ledger reading.

5. Good / Base / Bad Cases

  • Base: manager keeps payment review but cannot enter AP.
  • Good: CEO retains approval and payment review but cannot read AP without a separate finance role.
  • Bad: hiding a menu while leaving either backend read unrestricted.

6. Tests Required

Route injection checks both reads, filters and zero queries on refusal; role and navigation tests cover CEO and mixed roles. Browser checks cover manager direct refusal and preserved payment entry, plus allowed AP readers. Export and payment review regressions remain separate from ledger authorization.

7. Wrong vs Correct

Wrong: payment.batch.review implies all-company financial visibility, or a CEO role exception is added to AP handlers. Correct: use finance.ledger.view in role maps and read guards. This AP-only change does not alter the manager's six *.view_all permissions: reimbursement-list all-record visibility remains an explicit deferred decision, not a completed scope restriction.

Scenario: Bank Account Snapshots And Payment-Order Export

1. Scope / Trigger

  • Trigger: company payment-account maintenance, employee payee-bank maintenance, payment-batch creation, full-account reveal or bank payment-order export.
  • OA prepares payment evidence and a bank-import file; it does not call a bank payment API or treat the generated file as proof of payment.

2. Signatures

  • DB: BankInstitution, PaymentAccount, Employee.bank_institution_id, PaymentBatch.payment_account_snapshot_json, item snapshot_json.payee, and payment_order_export_* fields.
  • Reveal APIs use POST with JSON { "reason": "2-500 chars" }; ordinary read APIs return only bank name and masked account.
  • Payment-order API: POST /api/payment-batches/:id/payment-order with {}; success is an in-memory BIFF8 .xls download with Cache-Control: no-store.
  • Payment-order download confirms sensitive-file handling, but does not require a typed reason. Re-download warns about duplicate bank import/payment. Audit records system purpose reimbursement_bank_transfer with purposeSource: system; optional legacy user reasons remain genuine input, never fabricated. Account reveal, cancellation and receipt policies stay unchanged.
  • Filename is 报销制单_<batchNo>.xls, sent with RFC 5987 UTF-8 encoding and ASCII fallback. No names, amounts or account numbers enter the filename.
  • Runtime: Node passes validated row JSON through stdin to the fixed fill-payment-order-template.py; the dedicated Python runtime returns BIFF8 bytes on stdout. PAYMENT_ORDER_PYTHON_BIN may select the fixed interpreter, but requests cannot select the script or template path.

3. Contracts

  • Employee account editing remains HR/admin-owned. Never grant employee.bank.manage to finance, finance manager or system administrator.
  • Company account editing is finance-manager/admin-owned; ordinary finance is masked read plus reason-audited full reveal.
  • A new bank batch freezes the exact existing ciphertext for payer and payees. Copy ciphertext directly; decrypt only to calculate the masked suffix. Never decrypt and re-encrypt while building a snapshot.
  • Review UI reads masked values from the frozen snapshot. Finance manual-payment reveal also reads the frozen item snapshot, not the employee's current bank profile. An employee profile change after review must not change the payment target for that batch.
  • Ordinary batch, AP and export DTOs must explicitly select safe fields and must never return raw snapshot JSON, v1: ciphertext or a full account.
  • Payment-order preflight returns a safe blocker array containing fixed codes and, when useful, the reimbursement business number. Collect every batch and item blocker before returning; do not restore fail-fast "fix one, find one" behavior. The UI maps those fixed codes to natural Chinese and keeps manual payment visible for legacy or unsupported batches.
  • Legacy batches without ciphertext snapshots may still complete through manual payment plus receipt, but they can neither export a payment order nor reveal a supposed frozen account through a live-profile fallback.
  • A payment-order file is an efficiency artifact. A bank receipt remains the hard evidence required before registering bank payment complete.
  • Generate bank files by filling the controlled, blank BIFF8 template asset. Rebuilding a workbook with matching headers is forbidden when it loses the bank template's column widths, row heights, fonts, fills, borders or alignment. The asset contains headers and formatted blank rows only, and its SHA-256 is verified before every fill.
  • BIFF8 rich-text runs are part of the template contract. Open the template with formatting_info=True, copy every source font property, and explicitly call write_rich_text; xlutils.filter does not preserve those runs by default. Never replace this with a hard-coded "red" font approximation.
  • Node owns authorization, snapshot decryption, blockers, ordering, serials, amounts, audit and export counters. Python only copies the fixed template and fills the already validated 15-cell rows. Full accounts travel only on child stdin; never place them in argv, environment variables, logs, stderr or a temporary workbook file.
  • CI and the production image install xlrd, xlwt and xlutils from the same --require-hashes requirements. Do not fall back to a style-dropping generator when Python, a dependency or the template is unavailable.

4. Validation & Error Matrix

  • Missing/mixed legal entity, no unique active payer, missing bank institution, missing account or decrypt failure -> reject batch creation with 409 and no batch/item writes.
  • First release bank code is not CCB for any payer/payee -> reject the whole export; do not emit 02, skip a row or create a partial file.
  • Batch not reviewed, legacy snapshot or malformed snapshot -> reject export with 409; do not increment export count or send file bytes.
  • Reveal without permission or approved finance business role -> 403.
  • Reveal/export without a 2-500 character reason -> 400 VALID_001 and no full account/file response.
  • Successful export audit/update failure -> no download response.
  • Template hash/structure mismatch -> 409 PAYMENT_ORDER_EXPORT_BLOCKED, no export increment and no file bytes; manual payment plus receipt remains available.
  • Python executable/dependency missing, timeout, oversized output, non-zero exit or invalid OLE magic -> fixed safe generation error; never copy child stderr into the API error or application log.

5. Good/Base/Bad Cases

  • Good: review shows a masked frozen CCB account; finance supplies a reason and temporarily reveals that same frozen account for manual transfer.
  • Base: a historical batch displays compatibility metadata, proceeds by manual payment and receipt, and clearly has no payment-order capability.
  • Bad: export or manual-transfer reveal re-queries the employee profile after review, silently uses a changed account, or returns raw snapshot JSON.
  • Good: a fourth synthetic row copies the first template data-row style and height while the amount cell changes only to numeric 0.00 formatting.
  • Bad: create a fresh workbook from the 15 labels, or invoke Python with an account in argv/env, even if the resulting values look correct in a parser.

6. Tests Required

  • Permission matrix for employee/company account manage, masked read and full reveal; include an explicit system-admin denial.
  • Privacy-negative assertions over batch/list/detail/AP responses for raw accounts, v1: and raw snapshots.
  • Batch creation tests for single legal entity, direct ciphertext copy, CCB-only, decrypt failure and legacy behavior.
  • BIFF8 template-fill tests plus independent readback for three sheets, 15 headers, leading-zero 01, long account text, Chinese, numeric 0.00 amounts and deterministic row order. Compare visual signatures for column widths, row heights, font/bold, fill, border, alignment and wrapped text; include a row beyond the three preformatted template rows.
  • Compare rich-text segments semantically, including start offsets and complete font properties. Required headers must assert the first * uses the source red font and all remaining text uses the source black font; whole-cell style equality is insufficient.
  • Negative tests cover missing Python/dependencies, template hash drift, child failure and a child that echoes the synthetic input to stderr; exposed errors and logs must not contain the account or original row payload.
  • Build one synthetic batch with at least two independent payer/payee blockers; assert the DTO/error contains both safe blocker entries and emits no file, export counter or success audit.

7. Wrong vs Correct

Wrong

const liveEmployee = await prisma.employee.findUnique({ where: { id: ownerId } });
return decryptStringOrThrow(config, liveEmployee.bank_account_encrypted);

Correct

const encrypted = paymentBatchItem.snapshot_json?.payee?.bankAccountEncrypted;
assertReviewedFinanceReveal({ batch, actor, reason, encrypted });
await writeRevealAudit();
return decryptStringOrThrow(config, encrypted);

Scenario: Defensive Security Finding Summaries

1. Scope / Trigger

  • Trigger: a security review or remediation task writes findings into ordinary repository-visible Trellis material, specs, reports or management summaries.
  • This contract governs the shareable summary. It does not replace focused automated tests or the archived remediation task that carries engineering proof.

2. Signatures

  • Allowed summary fields: findingId, riskCategory, discoverySeverity, repo-relative codeLocations, businessImpact, controlObjective, safeVerification, remediationStatus, deploymentStatus, externalVerificationStatus, evidenceRef.
  • Forbidden summary fields: exploit procedure, payload, bypass sequence, probability/throughput calculation, raw request/response, real-environment identifier or secret-bearing value.

3. Contracts

  • Keep the finding ID, risk category, discovery severity, repo-relative code locations, business impact, control objective, safe verification result and the separate remediation/deployment/external states.
  • Do not store attack procedures, working payloads, bypass instructions, probability calculations, composable endpoint sequences, raw provider or browser payloads, or identifying details from a real environment.
  • Phrase verification as a harmless control assertion, such as an unauthorized request being rejected, active content being forced to a non-executable download, or a negative-path test passing with synthetic data.
  • Detailed engineering proof belongs in focused automated tests and the archived remediation task. Ordinary finding ledgers reference that evidence instead of copying sensitive mechanics.
  • If evidence cannot be safely summarized, keep only metadata and a restricted evidence reference; do not place the raw material in the repository or chat.

4. Validation & Error Matrix

  • Safe code location + defensive result -> allowed.
  • Real account, token, attachment, contract body, business payload or complete environment detail -> remove and regenerate the evidence.
  • Reproduction text that enables misuse -> replace with the control objective and a synthetic negative-path assertion.
  • Local remediation pass without deployment/public evidence -> keep deployment and external behavior pending.

5. Good/Base/Bad Cases

  • Good: identify the affected module, state the business impact and assert that a synthetic unauthorized or active-content negative path is rejected.
  • Base: an unresolved static risk keeps a safe code reference and control objective while external behavior remains gated.
  • Bad: copy a working request sequence, payload, real endpoint context or real data fragment into a PRD or finding ledger.

6. Tests Required

  • node scripts/qa/check-sensitive-content.mjs.
  • Manual review of security summaries for procedural or identifying detail.
  • Focused positive/negative automated tests using synthetic fixtures.

7. Wrong vs Correct

Wrong

{
  "findingId": "SEC-001",
  "reproduction": "step-by-step operational procedure",
  "rawPayload": "real or executable content"
}

Correct

{
  "findingId": "SEC-001",
  "riskCategory": "authorization boundary",
  "businessImpact": "data outside the actor's scope may be exposed",
  "controlObjective": "deny by default and enforce object scope",
  "safeVerification": "synthetic unauthorized request is rejected"
}

Scenario: Business Attachment Governance

1. Scope / Trigger

  • Trigger: adding or changing OA business attachments for reimbursement, purchase, payment, contract, project or company-document workflows.
  • Applies when an attachment is stored by the OA and can later be previewed, downloaded, reviewed or archived.
  • Do not introduce a generic file-center or free directory upload unless a Trellis task explicitly reopens that architecture.

2. Signatures

  • DB fields on business attachment models: governance_status, sensitivity_level, attachment_type, reviewed_by, reviewed_at, review_note, archived_by, archived_at, archive_note, download_policy.
  • Upload multipart fields may include: governanceStatus, sensitivityLevel, attachmentType, downloadPolicy.
  • API response fields must use camelCase: governanceStatus, sensitivityLevel, attachmentType, downloadPolicy, reviewedBy, reviewedAt, reviewNote, archivedBy, archivedAt, archiveNote.
  • Download APIs may accept reason; sensitive or reason-required attachments must reject downloads without a non-empty reason.
  • Upload content-policy inputs are { buffer, fileName, declaredMimeType, maxBytes, normalizeImages }; accepted output must contain canonical buffer, extension, mimeType, fileSize and fileSha256.
  • Browser downloads of OA-stored attachments must use a currently authenticated request, preferably an API-local POST with optional JSON { "reason": "..." }, rather than a query bearer URL. The only approved query-grant exception is the at-most-120-second, purpose/object/actor-bound grant used by the official WeCom previewFile native layer; follow wecom-boundary.md and do not reuse that grant for ordinary browser downloads or other integrations. Payment-batch receipt preview is a separate, authenticated domain route described below; it never uses a bearer query URL.

3. Contracts

  • Default status is approved unless the business workflow has a stricter state such as pending_review.
  • Default sensitivity is internal; payment receipts and similarly sensitive financial proof should use confidential.
  • index_only means OA stores the index/metadata, not the original file.
  • Frontend labels are informational only; backend permission and download reason checks remain authoritative.
  • Audit logs for downloads must include actor, business target, attachment ID, attachment type, sensitivity level, governance status and sanitized reason when provided.
  • New uploads must use an allowlist and jointly verify the final extension, declared MIME and actual bytes before storage. Original names are display metadata only; storage keys use a server-generated random identifier and a canonical extension.
  • PDF basic validation requires a strict header and terminal EOF boundary. Safe image formats must first match fixed signature bytes, then decode with Sharp using failOn: "warning", a fixed pixel ceiling and a one-page constraint; new images are re-encoded without metadata before size/hash/MIME persistence.
  • A governance state, existing hash, extension or stored MIME never proves historical content safe. Every download must re-authorize the current actor and business object, read under a hard byte limit and re-run the actual-byte policy before any response bytes are sent.
  • Successful download audit is part of authorization: write it before setting attachment headers or sending bytes. Audit failure must return a non-success response with no file body.
  • Successful ordinary browser downloads use application/octet-stream, attachment, nosniff, private no-store caching, no-referrer and a route-level sandbox CSP. Do not restore general same-origin inline preview or query-token download routes. The WeCom-native reimbursement invoice/attachment exception retains the domain-specific inline response and must still audit before bytes, use no-store/no-referrer and reject expired or mismatched grants.
  • The only ordinary-browser inline exception is GET /api/payment-batches/:id/receipt-attachments/:attachmentId/file?mode=preview. It remains batch- and department-authorized, revalidates bounded actual bytes, accepts only PDF/JPEG/PNG/WebP, writes PAYMENT_BATCH_RECEIPT_PREVIEWED with fixed reason 付款批次内在线预览 before sending bytes, and returns the validated MIME with inline, nosniff, private no-store, no-referrer, sandbox CSP and same-origin resource policy. Default/download mode keeps forced binary download and reason-required audit behavior. Do not generalize this exception into a reusable file-center preview route.

4. Validation & Error Matrix

  • Unknown governance enum in upload -> normalize to the module default, do not persist arbitrary strings.
  • Sensitive or reason_required attachment download without reason -> 400 VALID_001 with { reason: "required" }.
  • Missing attachment or missing stored object -> 404.
  • User lacks business read permission -> 403 PERM_001.
  • Download reason longer than the server limit -> trim before writing audit metadata.
  • Unsupported/active bytes, extension-MIME-content mismatch, dangerous file name, corrupt/truncated content, animation/multiple pages or pixel/byte limit breach -> reject before object-storage and database writes.
  • Explicitly quarantined, missing, oversized, corrupt or unverifiable stored object -> do not return file bytes; write a fixed blocked-reason audit where possible.
  • Payment receipt preview with a MIME outside PDF/JPEG/PNG/WebP -> blocked audit with fixed reason code; no inline bytes.
  • Successful-download audit write fails -> return 5xx before download headers or file bytes.

5. Good/Base/Bad Cases

  • Good: a bank receipt upload returns confidential, receipt, reason_required; download first asks for用途 and writes an audit log.
  • Good: an authorized payment-detail preview uses the fixed server purpose and writes its preview audit; downloading the same receipt still asks for用途.
  • Base: a reimbursement supporting material upload defaults to internal, supporting_material, standard and remains downloadable by authorized participants.
  • Good: an allowed image is signature-checked, strictly decoded, normalized, stored under a random canonical key, then later revalidated and returned only as a forced binary download.
  • Base: an allowed historical PDF remains byte-for-byte unchanged in storage; every access still revalidates it and forces attachment download.
  • Bad: a page hides the download button but the backend route still streams the file without permission or reason checks.
  • Bad: Enterprise WeChat messages or reports include original attachments instead of OA links and desensitized summaries.
  • Bad: trust multipart.mimetype, reuse an original basename in the storage path, or return a stored MIME with inline from the OA origin.

6. Tests Required

  • Prisma validate after schema or migration changes.
  • Backend route tests for default governance fields, custom multipart fields when exposed, sensitive-download reason rejection and audit extra metadata.
  • Content-policy tests for every allowed type plus active/unknown content, mismatch, truncation, warning-only JPEG corruption, animation/multiple pages, pixel/byte limits, metadata removal, random keys and safe disposition names.
  • Route tests must prove immediate bounded multipart consumption, rejection before storage/DB writes, current authentication and object authorization, historical revalidation, blocked-audit reasons, audit-before-send and the complete forced-download header contract.
  • Payment receipt tests additionally assert preview MIME/disposition/security headers, fixed-purpose audit-before-send, PDF/image allowlist, blocked files, and that default download still rejects a missing reason.
  • Frontend typecheck/build after API type changes.
  • Focused UI verification for pages that add attachment governance labels or download-reason interactions.
  • Frontend ordinary-download tests must assert authenticated JSON request + Blob browser download. WeCom-native preview tests instead assert the official bridge receives the exact short URL/name/size and that window.open is not used. Use only synthetic in-memory fixtures; production attachments and historical bulk scanning require a separate approval.

7. Wrong vs Correct

Wrong

const storageKey = `${businessId}/${originalFileName}`;
await storage.writeBuffer({ key: storageKey, buffer, contentType: clientMime });
return reply.type(clientMime).header("content-disposition", "inline").send(buffer);

Correct

const validated = await validateActualAttachmentBytes({
  buffer,
  fileName,
  declaredMimeType,
  normalizeImages: false,
});
const reason = sanitizeDownloadReason(request.body?.reason);
if (requiresDownloadReason(attachment) && !reason) {
  throw buildValidationError({ reason: "required" });
}
await prisma.auditLog.create({ data: buildDownloadAudit({ attachment, reason }) });
return reply
  .type("application/octet-stream")
  .header("content-disposition", buildSafeAttachmentDisposition(validated.originalName))
  .header("x-content-type-options", "nosniff")
  .header("cache-control", "private, no-store")
  .send(validated.buffer);

Scenario: Company Documents And OA Collaboration

1. Scope / Trigger

  • Trigger: mapping company electronic documents, policy files, operations guidance, employee collaboration tasks or external-review materials into OA.
  • Applies to docs, specs, evidence, tasks, OA attachments, Enterprise WeChat messages, AI/Codex-assisted processing and storage decisions.

2. Contracts

  • OA is the operations control layer, not the full original-document library.
  • The controlled company document master keeps full originals, historical packages and high-sensitivity materials.
  • OA may store indexes, business object links, status, responsibility, hash or checksum summaries, archive-location summaries, review results and audit evidence.
  • Necessary business originals may enter OA only through scoped business attachment flows such as contract, reimbursement, purchase, payment, project or policy workflows.
  • Employee collaboration, external review, generated reports, Enterprise WeChat messages and AI/Codex work use redacted copies, aliases or summaries.
  • Do not build or describe a generic file center unless a Trellis task explicitly reopens that architecture and defines permissions, audit, storage, backup and retention rules.

3. Storage Decision Defaults

  • Current V0 may keep authorized business attachments on the deployment server, but it must not become a shared drive or full document mirror.
  • If storage is insufficient, prefer index-only records plus controlled offline retrieval before moving high-sensitivity originals online.
  • Cloud object storage or controlled NAS/MinIO must be private, accessed by the OA service account, and governed by OA permissions and download audit.
  • A home NAS can be cold archive, secondary backup or low-risk S3-compatible trial storage; it is not the default production attachment store unless VPN, private bucket, UPS, alerts and restore drills are in place.

4. Forbidden Content

  • Full local paths to confidential directories.
  • Passwords, token, Secret, private keys, source packages and complete configuration files.
  • ID-card, bank-card, payroll, social-security, tax and full bank-statement originals in normal OA attachment areas.
  • Customer original material, sensitive quotes, contract bodies or signed packages outside authorized business flows.

Scenario: External Delivery Error Logging

1. Scope / Trigger

  • Trigger: logging SMTP, webhook, object-storage or other provider delivery failures whose native error objects may contain recipients or payloads.

2. Signatures

  • Email log metadata: errorCode, optional providerCode, optional smtpCommand, optional numeric smtpResponseCode, request/business IDs and recipient count.
  • Forbidden input: raw provider Error objects in structured logs.

3. Contracts

  • Log only explicitly allowlisted scalar metadata.
  • Do not log response, rejected, rejectedErrors, envelope, addresses, raw messages, headers, credentials or payloads.
  • Provider codes must match a bounded token format; SMTP commands must come from a fixed safe set; response codes must be bounded integers.
  • Delivery failure counts remain observable without exposing recipient data.

4. Validation & Error Matrix

  • Known safe provider code/command/response code -> include normalized fields.
  • Unknown or free-form field -> omit it.
  • Error with rejected address or SMTP response text -> log generic category and safe numeric/token fields only.
  • Missing logger -> return the delivery result without throwing a logging error.

5. Good/Base/Bad Cases

  • Good: record EENVELOPE, RCPT TO, 550, request ID and failed count.
  • Base: plain Error becomes only errorCode=email_delivery_failed.
  • Bad: pass Nodemailer's raw err to Pino, which serializes rejected addresses.

6. Tests Required

  • Mock a provider error containing a rejected full address, response and envelope.
  • Assert serialized log context contains none of those values and retains only allowlisted metadata.
  • Cover disabled, configured, success and failure delivery paths.

7. Wrong vs Correct

Wrong

logger.error({ err: providerError, recipient }, "delivery failed");

Correct

logger.error({ ...buildSafeErrorLog(providerError), requestId, failed }, "delivery failed");

Scenario: Current Fact Source Sensitive Gate

1. Scope / Trigger

  • Trigger: adding, registering or changing a CURRENT fact source, README, deployment entry or environment template.

2. Signatures

  • Gate: node scripts/qa/check-sensitive-content.mjs.
  • Registry: docs/文档事实源状态矩阵.md rows whose status is CURRENT and contract path is under docs/.
  • Output: fileRef, line, category; never the matched text.

3. Contracts

  • Scan fixed execution/deployment entrypoints plus every CURRENT docs contract derived from the status matrix; do not maintain a partial duplicate docs list.
  • Detect secret assignments, credential URLs, full accounts, personal workspace paths, private keys and public IPv4 values.
  • Allow explicit placeholders, environment interpolation and loopback addresses.
  • Missing registry files remain a docs-audit concern; existing scanned files must not be silently excluded.

4. Validation & Error Matrix

  • Any sensitive match -> FAIL with non-zero findingCount.
  • CURRENT doc added to the matrix -> automatically enters the scan set.
  • EVIDENCE/HISTORICAL issue row -> does not enter the CURRENT docs scan set.
  • Placeholder such as <repo-root> -> pass.

5. Good/Base/Bad Cases

  • Good: all current docs and deploy entrypoints scan clean with metadata-only output.
  • Base: historical evidence stays outside this current-source blocking gate.
  • Bad: hard-code six docs while the matrix contains eighteen CURRENT docs.

6. Tests Required

  • Parser fixture with CURRENT doc, historical doc and CURRENT Issue row.
  • Assertion that representative current manuals/specs appear in the resolved default set.
  • Repository gate must report findingCount=0 and the expected expanded file count after registry changes.

7. Wrong vs Correct

Wrong

const files = ["docs/文档索引.md", "docs/产品设计规则.md"];

Correct

const files = [...fixedEntrypoints, ...extractCurrentDocumentFiles(statusMatrix)];