| name | platform-pre-review |
| description | Platform Developer: Stage 4a pre-review task. Run lint, type-check, SCA, license check, security trigger, and hygiene checks. Produce pipeline/gates/stage-04a.json. |
Platform Pre-Review Task (Stage 4a)
Use this skill when you are the Platform Developer executing the Stage 4a
pre-review gate — after all Stage 4 build gates pass and before Stage 5
peer review starts.
Procedure
-
npm run lint (or the project's equivalent) — must exit 0.
-
The project's type-check command if one is configured (e.g. tsc --noEmit for TypeScript projects) — must exit 0.
-
Dependency vulnerability scan: npm audit --audit-level=high (or
pip-audit, bundler-audit, etc. per stack). Any high or
critical finding halts.
-
License compatibility check. For every new or changed direct dependency
(compare package.json / requirements.txt / Cargo.toml before and
after the PR), determine its declared SPDX license and classify it:
- Allowed (record nothing): MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause,
ISC, CC0-1.0, 0BSD, Unlicense, CC-BY-4.0, Python-2.0, PSF-2.0.
- Warned (record with
policy: "warned"): UNLICENSED, unknown,
proprietary, SSPL-1.0, BUSL-1.1. These require a human review before
merge; record in license_findings[] and add a warnings[] entry.
- Denied (record with
policy: "denied"): any GPL-2.0, GPL-3.0,
LGPL-2.0, LGPL-2.1, LGPL-3.0, AGPL-3.0, or strong-copyleft variant.
Copyleft licenses require distributing source and are incompatible with
most commercial projects unless a legal exception is documented. A denied
finding sets license_check_passed: false and adds a blockers[] entry.
How to check:
npx license-checker --direct --json 2>/dev/null | jq 'to_entries[] | {package: .key, license: .value.licenses}'
pip-licenses --format=json --with-license-file 2>/dev/null
cargo license --json 2>/dev/null
If no automated tool is available, manually inspect each new dependency's
LICENSE file or package metadata. When the license field is missing or
ambiguous, classify as warned.
If the project has a .devteam/config.yml license.extra_allowed list,
include those SPDX identifiers as allowed. Example config override:
license:
extra_allowed: ["LGPL-2.1"]
Record only non-allowed packages in license_findings[]. Set
license_check_passed: true when no findings have policy: "denied";
set it false if any do.
-
Apply the security heuristic (npm run security:check -- <changed-files>).
Record "security_review_required": true | false in the Stage 4a gate.
-
Platform hygiene checks — these catch problems that reviewers consistently
flag in Stage 5 and that have clear, mechanical fixes:
a. Runtime engine constraint. If any ADR specifies a minimum runtime
version (e.g., "Node.js LTS v20+"), verify package.json carries a
matching "engines" field:
{ "engines": { "node": ">=20" } }
Missing or wrong engines when an ADR requires it → BLOCKER in the
Stage 4a gate; the ADR is unenforceable without it.
b. Test coverage output in .gitignore. If the project's test runner is
configured to write a coverage directory (collectCoverage, coverageDirectory,
or equivalent), verify .gitignore excludes it. A coverage directory not
in .gitignore will be committed by accident and diverge across branches.
Missing .gitignore entry → BLOCKER.
c. Duplicate config files. If the same tool has config at more than one
path (e.g., both .eslintrc.js at root and src/infra/eslint.config.js),
both must be documented and cross-referenced, or one must be deleted. An
undocumented duplicate with diverging settings silently affects different
parts of the codebase differently. Undocumented duplicate → BLOCKER.
d. package.json bin target exists and is owned. If package.json has a
bin field, verify the target file path exists in the project AND that it
is listed under exactly one workstream's area in the design spec's
## File Ownership table. A bin target pointing to a file not in any
workstream's files_written[] is a dead entry — record as BLOCKER.
Capture output to pipeline/lint-output.txt and pipeline/pre-review-output.txt.
Write pipeline/gates/stage-04a.json:
{
"stage": "stage-04a",
"status": "PASS" | "FAIL",
"workstream": "platform",
"timestamp": "<ISO>",
"track": "<track>",
"lint_passed": true,
"tests_passed": true,
"type_check_passed": true,
"sca_findings": { "high": 0, "critical": 0 },
"dependency_review_passed": true,
"license_check_passed": true,
"license_findings": [],
"security_review_required": false,
"blockers": [],
"warnings": []
}
Orchestrator-stamped fields. The orchestrator runs the configured lint
and test commands itself after this stage and overwrites lint_passed and
tests_passed based on what it actually observes (exit code 0 vs non-zero).
The stamp records the result in _orchestrator_stamped for audit. If
your assertion disagrees with what the orchestrator observes (e.g., you
wrote lint_passed: true but the lint command returns non-zero), the
orchestrator's truth wins and the gate's status flips to FAIL. Be
honest in your initial write — devteam verify stage-04a will catch a
lie, and the audit trail will record both your claim and the override.
If any check fails, the owning dev is invoked to fix. Stage 5 peer review
does not start until this gate passes.
Rationale: a reviewer reading code that doesn't even lint is wasting tokens
on problems the toolchain already knows about.