Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@
## Required validation limits

Follow [validation policy](docs/validation-policy.md), which supersedes older runtime and release-test requirements. Never add or execute macOS CI, device/emulator/GUI/browser E2E CI, live service or inference CI, installed-consumer CI or public-download verification. Keep runtime checks explicit local opt-in. Do not repeat public archive/hash checks, passing tests or post-merge runtime cycles. Preserve lock/signature/licence/provenance checks at actual trust handoffs. Do not invoke wsl.exe, configure proxy 7890 or install toolchains solely for testing. Stop and report the exact failed network operation. Hooks do not rebuild/test on commit or push.

Dependency additions and upgrades follow [the enforced admission policy](docs/dependency-policy.md); update its input-bound review and retain the existing class and provenance gates.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ Describe the problem, resulting behavior and concrete validation in the PR. Dist
Contributions to original repository code and tooling are provided under the existing AGPL-3.0-only license. Preserve dependency licenses and notices. Adding a reference project's feature does not authorize copying its implementation or changing product scope.

Follow the [provenance process](docs/provenance.md) before reusing source, tests, legal text or generated resources. Source and actual candidate checks require complete reviewed records, immutable history and preserved full notices.

Dependency additions and upgrades follow [the enforced admission policy](docs/dependency-policy.md); update its input-bound review and retain the existing class and provenance gates.
15 changes: 15 additions & 0 deletions docs/dependency-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Dependency admission

`npm run check:dependencies` enforces `eng/policy/dependency-policy.json` against the actual workspace manifests, complete external npm lock closure, source imports and reviewed dependency input hashes. `npm run test:dependencies` contains offline refusal fixtures. Both are part of the existing source check; no extra CI job or runtime test is introduced.

The inventory preserves exact registry URLs, versions, integrity and declared licence expressions. These expressions identify locked build inputs; they do not authorize redistribution by themselves. Existing source provenance and immutable shipped-artifact profiles remain mandatory file-level licence, source-hash, generator, notice and output-closure authorities. Build tooling, including LGPL/MPL components, does not become shipped product code through this inventory. New native capability admission requires AD-01 through AD-08 and a reviewed owning artifact profile.

Dependency changes must update manifests and locks together, inspect the exact upstream terms and transitive closure, and update the input-bound owner/reviewer/date/rationale record. Retain actual maintenance, compatibility, security, SBOM and class compilation assessments. Framework majors also require an explicit runtime/native/transport and migration assessment. Runtime/performance diagnostics are local opt-in only for affected behavior supported by the existing environment; absent coverage is reported, never silently passed or provisioned as a new task. Existing provenance records remain append-only; use successors when their inputs change. A class or artifact-profile change requires review.

Public Contracts packages come only from `ArcForges/Contracts` through the exact npm registry coordinates and committed SHA512 locks. Source imports, re-exports and literal dynamic imports cannot reference private Contracts packages/subpaths or escape into a sibling checkout. Current owners have not adopted an internal generated package. Future internal consumers need an explicit owner admission and compatible licence boundary; public clients remain excluded. Package publisher ownership is an admission assertion reviewed against the existing producer receipt, not a claim that npm lock integrity independently authenticates an account.

Existing exact prerelease Contracts versions are named foundation candidate inputs only. A stable closure rejects them. Mutable tags such as `latest`, ranges, local paths and Git selectors are not consumer coordinates. Candidate/stable promotion uses the original sealed bytes and existing provider identity handoff; no public package downloads, replacement versions or tags are used for policy validation. This repository does not publish tooling packages. Its existing `cloudflare` environment restricts deployment credentials to the trusted main workflow; PR checks have no publisher credentials. Publication success remains provider status, not live runtime acceptance.

Policy edits require normal complete PR review; refreshing a hash alone is not admission. The offline gate proves matching evidence and refusal behavior, not independent authorship, legal approval or commercial acceptance.

Each admission has an immutable `eng/policy/dependency-reviews/` receipt. A successor names the retained predecessor. The checker compares every historical receipt with its original committed bytes and rejects changed integrity for any previously admitted name/version even when the new policy, lock and review all agree. Missing or edited historical receipts fail.
136 changes: 136 additions & 0 deletions eng/dependency-policy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// SPDX-License-Identifier: AGPL-3.0-only
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import path from "node:path";
import test from "node:test";
import {
auditDependencies,
digest,
immutableCoordinates,
validateHistoricalCoordinates,
validateClosure,
validateImports,
validatePolicy,
type Policy,
} from "./dependency-policy.ts";

const root = process.cwd();
const baseline = JSON.parse(readFileSync("eng/policy/dependency-policy.json", "utf8")) as Policy;
const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
test("actual repository admission", () => {
assert.equal(auditDependencies(root).result, "passed");
});
test("forbidden licence", () => {
const packages = structuredClone(lock.packages);
packages[Object.keys(baseline.closure)[0]!].license = "GPL-3.0-only";
assert.throws(() => validateClosure(baseline, packages), /Forbidden licence/u);
});
test("floating tag", () => {
const packages = structuredClone(lock.packages);
packages[Object.keys(baseline.closure)[0]!].version = "latest";
assert.throws(() => validateClosure(baseline, packages), /Floating version/u);
});
test("same version with altered bytes", () => {
const packages = structuredClone(lock.packages);
packages[Object.keys(baseline.closure)[0]!].integrity =
`sha512-${Buffer.alloc(64).toString("base64")}`;
assert.throws(() => validateClosure(baseline, packages), /mutable-version/u);
});
test("wrong publisher", () => {
const policy = structuredClone(baseline);
policy.firstParty["@arcforges/proto"] = { publisher: "other/Contracts", visibility: "public" };
assert.throws(() => validatePolicy(policy), /Wrong publisher/u);
});
test("untrusted registry", () => {
const packages = structuredClone(lock.packages);
packages[Object.keys(baseline.closure)[0]!].resolved =
"https://registry.npmjs.org.evil.invalid/p.tgz";
assert.throws(
() => validateClosure(baseline, packages),
/Untrusted feed|Wrong publisher artifact/u,
);
});
test("upgrade needs input-bound review and framework assessment", () => {
const policy = structuredClone(baseline);
policy.inputs["package-lock.json"] = "0".repeat(64);
assert.throws(() => validatePolicy(policy), /Upgrade review/u);
policy.review.inputs = structuredClone(policy.inputs);
policy.review.runtimeAssessment = "";
assert.throws(() => validatePolicy(policy), /runtimeAssessment/u);
});
test("native additions need adoption evidence", () => {
const policy = structuredClone(baseline);
policy.native.mode = "anything-installed";
assert.throws(() => validatePolicy(policy), /Native adoption/u);
});
test("public sources reject static, re-export and dynamic internal imports", () => {
for (const source of [
'import x from "@arcforges/ai-internal";',
'export * from "@arcforges/proto/internal";',
'void import("@arcforges/storage-internal");',
'const x = require("@arcforges/ai-internal");',
])
assert.throws(() => validateImports(root, "src/example.ts", source, baseline), /[Ii]nternal/u);
});
test("source cannot escape into a sibling repository", () => {
assert.throws(
() => validateImports(root, "example.ts", 'import "../Contracts/private.ts";', baseline),
/Sibling source/u,
);
validateImports(root, "src/example.ts", 'import type { X } from "@arcforges/proto";', baseline);
});
test("stable closure cannot inherit foundation candidates", () => {
const policy = structuredClone(baseline);
policy.channel = "stable";
assert.throws(() => validateClosure(policy, lock.packages), /Prerelease/u);
});
test("input hashes are portable, content sensitive and independent of outer metadata", () => {
assert.equal(digest("a\r\n"), digest("a\n"));
assert.notEqual(digest("a\n"), digest("b\n"));
assert(path.isAbsolute(root));
});

test("reviewed successor cannot replace bytes for an admitted coordinate", () => {
assert.throws(
() =>
validateHistoricalCoordinates(
{ "example@1.0.0": "sha512-original" },
{ "example@1.0.0": "sha512-replacement" },
),
/despite successor review/u,
);
validateHistoricalCoordinates(
{ "example@1.0.0": "sha512-original" },
{ "example@1.0.1": "sha512-new-version" },
);
});
test("comments and escaped module names do not bypass public import scope", () => {
assert.throws(
() =>
validateImports(
root,
"src/example.ts",
'import /* comment */ "@arcforges/ai-internal";',
baseline,
),
/internal/u,
);
assert.throws(
() => validateImports(root, "src/example.ts", "void import(`@arcforges/${kind}`);", baseline),
/Computed import/u,
);
});

test("duplicate nested coordinates cannot hide different package bytes", () => {
assert.throws(
() =>
immutableCoordinates({
"node_modules/example": { version: "1.0.0", integrity: "sha512-first" },
"node_modules/parent/node_modules/example": {
version: "1.0.0",
integrity: "sha512-second",
},
}),
/Conflicting integrity/u,
);
});
Loading
Loading