@echopath-labs/forgerail 0.1.0-alpha.1 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +7 -3
- package/CHANGELOG.md +8 -0
- package/PLUGIN.md +2 -1
- package/PRIVACY.md +15 -0
- package/README.md +14 -2
- package/README.zh-CN.md +13 -2
- package/TERMS.md +19 -0
- package/adapters/codex.json +1 -1
- package/assets/forgerail-icon.svg +6 -0
- package/assets/forgerail-logo.svg +7 -0
- package/contracts/authority-evidence-v1.schema.json +48 -0
- package/contracts/cross-workspace-pack-composition-v1.schema.json +241 -0
- package/contracts/effective-profile-v2.schema.json +113 -0
- package/contracts/entry-mode-v1.schema.json +20 -0
- package/contracts/evidence-identity-v1.schema.json +48 -0
- package/contracts/execution-context-identity-v1.schema.json +76 -0
- package/contracts/gate-result-v1.schema.json +32 -0
- package/contracts/governance-source-v1.schema.json +30 -0
- package/contracts/host-adapter-observation-v1.schema.json +61 -0
- package/contracts/limited-reason-v1.schema.json +46 -0
- package/contracts/operation-authority-requirement-v1.schema.json +43 -0
- package/contracts/operation-grant-v1.schema.json +83 -0
- package/contracts/phase-slice-correlation-v1.schema.json +20 -0
- package/contracts/profile-explanation-v1.schema.json +91 -0
- package/contracts/provider-adapter-observation-v1.schema.json +87 -0
- package/contracts/return-receipt-v2.schema.json +55 -0
- package/contracts/review-authority-requirement-v1.schema.json +117 -0
- package/contracts/rollback-envelope-lineage-v1.schema.json +46 -0
- package/contracts/rule-claim-v1.schema.json +34 -0
- package/contracts/source-dependency-edge-v1.schema.json +37 -0
- package/contracts/task-control-revision-v1.schema.json +39 -0
- package/contracts/task-envelope-v2.schema.json +67 -0
- package/contracts/validation-result-v1.schema.json +57 -0
- package/contracts/validation-topology-v1.schema.json +55 -0
- package/contracts/workspace-identity-v1.schema.json +28 -0
- package/contracts/workspace-relationship-v1.schema.json +31 -0
- package/directory/README.md +23 -0
- package/directory/evaluations.json +78 -0
- package/directory/release-notes-alpha2.md +7 -0
- package/directory/submission-candidate.json +80 -0
- package/docs/composable-autonomy.zh-CN.md +183 -0
- package/docs/control-authority-validation-contracts.md +36 -0
- package/docs/control-authority-validation-contracts.zh-CN.md +36 -0
- package/docs/control-profile-contracts.md +35 -0
- package/docs/control-profile-contracts.zh-CN.md +35 -0
- package/docs/control-system-fixture-matrix.md +30 -0
- package/docs/control-system-fixture-matrix.zh-CN.md +30 -0
- package/docs/control-task-contracts.md +39 -0
- package/docs/control-task-contracts.zh-CN.md +39 -0
- package/docs/cross-workspace-pack-composition-contract.md +31 -0
- package/docs/cross-workspace-pack-composition-contract.zh-CN.md +31 -0
- package/docs/installation.md +9 -3
- package/docs/installation.zh-CN.md +9 -3
- package/docs/release-alpha2.md +21 -0
- package/docs/release-alpha2.zh-CN.md +21 -0
- package/package.json +9 -3
- package/packs/architecture-convergence-audit.json +30 -0
- package/scripts/build-universal-directory-candidate.mjs +72 -0
- package/scripts/disposable-consumer.mjs +1 -1
- package/scripts/fixtures/contracts/authority-evidence.revoked-without-invalidation.invalid.json +22 -0
- package/scripts/fixtures/contracts/authority-evidence.revoked.valid.json +32 -0
- package/scripts/fixtures/contracts/authority-evidence.valid.json +22 -0
- package/scripts/fixtures/contracts/capability-pack.missing-entry.invalid.json +14 -0
- package/scripts/fixtures/contracts/capability-pack.valid.json +15 -0
- package/scripts/fixtures/contracts/cross-workspace-pack-composition.authority-collapse.invalid.json +156 -0
- package/scripts/fixtures/contracts/cross-workspace-pack-composition.false-closure.invalid.json +138 -0
- package/scripts/fixtures/contracts/cross-workspace-pack-composition.valid.json +156 -0
- package/scripts/fixtures/contracts/effective-profile-v2.optional-unavailable-degraded.valid.json +36 -0
- package/scripts/fixtures/contracts/effective-profile-v2.required-unavailable-degraded.invalid.json +36 -0
- package/scripts/fixtures/contracts/effective-profile-v2.unresolved-claim-false-complete.invalid.json +34 -0
- package/scripts/fixtures/contracts/effective-profile-v2.valid.json +53 -0
- package/scripts/fixtures/contracts/effective-profile.duplicate-rule.invalid.json +23 -0
- package/scripts/fixtures/contracts/entry-mode.missing-evidence.invalid.json +6 -0
- package/scripts/fixtures/contracts/entry-mode.valid.json +6 -0
- package/scripts/fixtures/contracts/evidence-identity.unavailable-without-reason.invalid.json +21 -0
- package/scripts/fixtures/contracts/evidence-identity.valid.json +21 -0
- package/scripts/fixtures/contracts/execution-context-identity.unavailable-without-reason.invalid.json +31 -0
- package/scripts/fixtures/contracts/execution-context-identity.valid.json +31 -0
- package/scripts/fixtures/contracts/gate-result.satisfied-without-evidence.invalid.json +14 -0
- package/scripts/fixtures/contracts/gate-result.valid.json +14 -0
- package/scripts/fixtures/contracts/governance-source.unavailable-without-reason.invalid.json +16 -0
- package/scripts/fixtures/contracts/governance-source.valid.json +16 -0
- package/scripts/fixtures/contracts/host-adapter-observation.claims-authorization.invalid.json +23 -0
- package/scripts/fixtures/contracts/host-adapter-observation.valid.json +39 -0
- package/scripts/fixtures/contracts/host-adapter.codex.valid.json +1 -1
- package/scripts/fixtures/contracts/host-adapter.false-supported.invalid.json +14 -0
- package/scripts/fixtures/contracts/host-binding-receipt.valid.json +1 -1
- package/scripts/fixtures/contracts/launch-contract.execution-owner.invalid.json +19 -0
- package/scripts/fixtures/contracts/limited-reason.unbounded.invalid.json +11 -0
- package/scripts/fixtures/contracts/limited-reason.valid.json +11 -0
- package/scripts/fixtures/contracts/operation-authority-requirement.no-issuer.invalid.json +22 -0
- package/scripts/fixtures/contracts/operation-authority-requirement.valid.json +22 -0
- package/scripts/fixtures/contracts/operation-grant.missing-issuer-evidence.invalid.json +34 -0
- package/scripts/fixtures/contracts/operation-grant.valid.json +34 -0
- package/scripts/fixtures/contracts/phase-slice-correlation.aggregate-closure.invalid.json +12 -0
- package/scripts/fixtures/contracts/phase-slice-correlation.valid.json +12 -0
- package/scripts/fixtures/contracts/profile-change-candidate.unconfirmed.invalid.json +13 -0
- package/scripts/fixtures/contracts/profile-explanation.false-complete.invalid.json +36 -0
- package/scripts/fixtures/contracts/profile-explanation.valid.json +29 -0
- package/scripts/fixtures/contracts/provider-adapter-observation.unauthenticated-supported.invalid.json +43 -0
- package/scripts/fixtures/contracts/provider-adapter-observation.valid.json +62 -0
- package/scripts/fixtures/contracts/return-receipt-v2.changed-subject.valid.json +36 -0
- package/scripts/fixtures/contracts/return-receipt-v2.unverified-closeout.invalid.json +36 -0
- package/scripts/fixtures/contracts/return-receipt-v2.valid.json +36 -0
- package/scripts/fixtures/contracts/review-authority-requirement.transitive.invalid.json +33 -0
- package/scripts/fixtures/contracts/review-authority-requirement.valid.json +21 -0
- package/scripts/fixtures/contracts/rollback-envelope-lineage.reused-grant.invalid.json +24 -0
- package/scripts/fixtures/contracts/rollback-envelope-lineage.valid.json +24 -0
- package/scripts/fixtures/contracts/rule-claim.inferred-enforceable.invalid.json +16 -0
- package/scripts/fixtures/contracts/rule-claim.valid.json +16 -0
- package/scripts/fixtures/contracts/source-dependency-edge.unavailable-without-reason.invalid.json +18 -0
- package/scripts/fixtures/contracts/source-dependency-edge.valid.json +18 -0
- package/scripts/fixtures/contracts/task-control-revision.closed-without-receipt.invalid.json +37 -0
- package/scripts/fixtures/contracts/task-control-revision.valid.json +37 -0
- package/scripts/fixtures/contracts/task-envelope-v2.missing-workspace-identity.invalid.json +39 -0
- package/scripts/fixtures/contracts/task-envelope-v2.valid.json +54 -0
- package/scripts/fixtures/contracts/task-envelope-v2.workspace-mismatch.invalid.json +40 -0
- package/scripts/fixtures/contracts/validation-result.passed-without-evidence.invalid.json +29 -0
- package/scripts/fixtures/contracts/validation-result.valid.json +19 -0
- package/scripts/fixtures/contracts/validation-topology.dangling-requirement.invalid.json +32 -0
- package/scripts/fixtures/contracts/validation-topology.protected-environment.valid.json +32 -0
- package/scripts/fixtures/contracts/validation-topology.valid.json +32 -0
- package/scripts/fixtures/contracts/workspace-identity.missing-root.invalid.json +6 -0
- package/scripts/fixtures/contracts/workspace-identity.valid.json +14 -0
- package/scripts/fixtures/contracts/workspace-relationship.inferred-authority.invalid.json +13 -0
- package/scripts/fixtures/contracts/workspace-relationship.valid.json +13 -0
- package/scripts/fixtures/control-system/architecture-convergence-cases.json +25 -0
- package/scripts/fixtures/control-system/task-2.9-invalid-boundary-matrix.json +203 -0
- package/scripts/fixtures/control-system/task-4-diagnosis-health-separation.json +19 -0
- package/scripts/fixtures/workspaces/no-node-project/AGENTS.md +3 -0
- package/scripts/fixtures/workspaces/no-node-project/README.md +3 -0
- package/scripts/fixtures/workspaces/no-node-project/docs/adr/0001-durable-records.md +3 -0
- package/scripts/forgerail.mjs +467 -14
- package/scripts/lib/contracts.mjs +1132 -2
- package/scripts/shadow-comparison.mjs +1 -1
- package/scripts/validate-release.mjs +15 -15
- package/scripts/validate-universal-directory.mjs +151 -0
- package/skills/architecture-convergence-audit/SKILL.md +31 -0
- package/skills/architecture-convergence-audit/agents/openai.yaml +7 -0
- package/skills/architecture-convergence-audit/assets/github-issue-template.md +26 -0
- package/skills/architecture-convergence-audit/references/audit-contract.md +62 -0
- package/skills/workspace-health-review/references/health-review.md +15 -0
|
@@ -87,7 +87,7 @@ for (const scenario of scenarios) {
|
|
|
87
87
|
const result = {
|
|
88
88
|
schemaVersion: "1.0",
|
|
89
89
|
agwBaseline: "plugins/agent-workflow-governance@0.2.0-canonical",
|
|
90
|
-
forgeRailCandidate: "plugins/forgerail@0.1.0-alpha.
|
|
90
|
+
forgeRailCandidate: "plugins/forgerail@0.1.0-alpha.2-canonical",
|
|
91
91
|
scenarios,
|
|
92
92
|
covered: scenarios.filter((item) => item.passed).length,
|
|
93
93
|
unresolved: scenarios.filter((item) => !item.passed).map((item) => item.id),
|
|
@@ -6,9 +6,9 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
|
|
7
7
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
8
8
|
const expectedPackageName = "@echopath-labs/forgerail";
|
|
9
|
-
const expectedVersion = "0.1.0-alpha.
|
|
9
|
+
const expectedVersion = "0.1.0-alpha.2";
|
|
10
10
|
const expectedTag = `v${expectedVersion}`;
|
|
11
|
-
const expectedDate = "2026-08-
|
|
11
|
+
const expectedDate = "2026-08-30";
|
|
12
12
|
const expectedPlugins = [
|
|
13
13
|
"forgerail",
|
|
14
14
|
"forgerail-cross-workspace-orchestration",
|
|
@@ -47,7 +47,7 @@ record("package-lock-license", packageLock.packages?.[""]?.license === "Apache-2
|
|
|
47
47
|
record("npm-next-tag", packageJson.publishConfig?.tag === "next", packageJson.publishConfig?.tag ?? null);
|
|
48
48
|
record(
|
|
49
49
|
"prepublish-gate",
|
|
50
|
-
["npm test", "npm run test:shadow", "npm run test:release", "npm run test:consumer"].every((command) => packageJson.scripts?.prepublishOnly?.includes(command)),
|
|
50
|
+
["npm test", "npm run test:shadow", "npm run test:release", "npm run test:consumer", "npm run test:directory"].every((command) => packageJson.scripts?.prepublishOnly?.includes(command)),
|
|
51
51
|
packageJson.scripts?.prepublishOnly ?? null,
|
|
52
52
|
);
|
|
53
53
|
|
|
@@ -101,7 +101,7 @@ for (const name of expectedPlugins) {
|
|
|
101
101
|
|
|
102
102
|
const installation = `${read("docs/installation.md")}\n${read("docs/installation.zh-CN.md")}`;
|
|
103
103
|
for (const phrase of [
|
|
104
|
-
|
|
104
|
+
"codex plugin marketplace add echopath-labs/forgerail --ref v0.1.0-alpha.1",
|
|
105
105
|
"codex plugin add forgerail@echopath-labs",
|
|
106
106
|
"codex plugin add forgerail-cross-workspace-orchestration@echopath-labs",
|
|
107
107
|
"codex plugin add forgerail-github-rulesets@echopath-labs",
|
|
@@ -136,8 +136,8 @@ record("package-adapters", packageJson.files?.includes("adapters/"), packageJson
|
|
|
136
136
|
record("package-templates", packageJson.files?.includes("templates/"), packageJson.files ?? null);
|
|
137
137
|
record("no-apply-adoption-script", !read("scripts/forgerail.mjs").includes('command === "apply-adoption"'), "no apply-adoption command");
|
|
138
138
|
|
|
139
|
-
const releaseEnglish = read("docs/release.md");
|
|
140
|
-
const releaseChinese = read("docs/release.zh-CN.md");
|
|
139
|
+
const releaseEnglish = read("docs/release-alpha2.md");
|
|
140
|
+
const releaseChinese = read("docs/release-alpha2.zh-CN.md");
|
|
141
141
|
const releaseDocs = `${releaseEnglish}\n${releaseChinese}`;
|
|
142
142
|
for (const phrase of [
|
|
143
143
|
"remote_integration_approval",
|
|
@@ -146,7 +146,7 @@ for (const phrase of [
|
|
|
146
146
|
expectedVersion,
|
|
147
147
|
expectedTag,
|
|
148
148
|
"Node.js 22 and 24",
|
|
149
|
-
"
|
|
149
|
+
"codex/forgerail-alpha2-scoped",
|
|
150
150
|
"Do not unpublish",
|
|
151
151
|
"AGW",
|
|
152
152
|
"Host Binding Receipt",
|
|
@@ -156,17 +156,17 @@ for (const phrase of [
|
|
|
156
156
|
}
|
|
157
157
|
for (const [id, document, phrases] of [
|
|
158
158
|
["english", releaseEnglish, [
|
|
159
|
-
"The
|
|
160
|
-
"an ordinary
|
|
161
|
-
"
|
|
162
|
-
"
|
|
159
|
+
"The public candidate is an ordinary child of the observed remote `main`.",
|
|
160
|
+
"an ordinary source-first successor commit",
|
|
161
|
+
"The Draft PR base and publication comparison baseline remain bound to the observed remote `main`",
|
|
162
|
+
"The merged public `main` tree must equal the final signed projection tree",
|
|
163
163
|
"Install and discover each external Capability Pack separately",
|
|
164
164
|
]],
|
|
165
165
|
["chinese", releaseChinese, [
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"PR base 与 publication comparison baseline
|
|
169
|
-
"
|
|
166
|
+
"公共候选是已观测远端 `main` 的普通子 commit。",
|
|
167
|
+
"普通的 source-first successor commit",
|
|
168
|
+
"Draft PR base 与 publication comparison baseline 继续绑定已观测远端 `main`",
|
|
169
|
+
"合并后的公共 `main` tree 必须等于最终签名 projection tree",
|
|
170
170
|
"每个外部 Capability Pack 分别安装与发现",
|
|
171
171
|
]],
|
|
172
172
|
]) {
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
5
|
+
import { dirname, relative, resolve, sep } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { diagnoseWorkspace } from "./lib/diagnosis.mjs";
|
|
8
|
+
|
|
9
|
+
const pluginRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const readJson = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
11
|
+
const manifest = readJson(resolve(pluginRoot, ".codex-plugin/plugin.json"));
|
|
12
|
+
const packageJson = readJson(resolve(pluginRoot, "package.json"));
|
|
13
|
+
const candidate = readJson(resolve(pluginRoot, "directory/submission-candidate.json"));
|
|
14
|
+
const evaluations = readJson(resolve(pluginRoot, "directory/evaluations.json"));
|
|
15
|
+
const fixture = resolve(pluginRoot, "scripts/fixtures/workspaces/no-node-project");
|
|
16
|
+
const checks = [];
|
|
17
|
+
|
|
18
|
+
function assert(condition, message) {
|
|
19
|
+
if (!condition) throw new Error(message);
|
|
20
|
+
checks.push(message);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function hashTree(root) {
|
|
24
|
+
const entries = [];
|
|
25
|
+
function visit(current) {
|
|
26
|
+
for (const entry of readdirSync(current, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
27
|
+
const full = resolve(current, entry.name);
|
|
28
|
+
const rel = relative(root, full).split(sep).join("/");
|
|
29
|
+
if (entry.isDirectory()) visit(full);
|
|
30
|
+
else {
|
|
31
|
+
assert(!lstatSync(full).isSymbolicLink(), `fixture contains no symlink: ${rel}`);
|
|
32
|
+
const content = readFileSync(full);
|
|
33
|
+
entries.push({ path: rel, bytes: content.length, sha256: createHash("sha256").update(content).digest("hex") });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
visit(root);
|
|
38
|
+
return entries;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateAsset(label, assetPath) {
|
|
42
|
+
assert(typeof assetPath === "string", `${label} is a string`);
|
|
43
|
+
assert(assetPath.startsWith("./assets/"), `${label} starts with ./assets/`);
|
|
44
|
+
assert(!assetPath.split("/").includes(".."), `${label} contains no parent traversal`);
|
|
45
|
+
const resolved = resolve(pluginRoot, assetPath);
|
|
46
|
+
assert(resolved.startsWith(`${pluginRoot}${sep}`), `${label} remains inside the Plugin root`);
|
|
47
|
+
assert(existsSync(resolved), `${label} exists`);
|
|
48
|
+
const svg = readFileSync(resolved, "utf8");
|
|
49
|
+
assert(svg.includes("<svg") && svg.includes("</svg>"), `${label} is parseable SVG source`);
|
|
50
|
+
assert(!/(?:href|src)\s*=\s*["'](?:https?:|data:)/i.test(svg), `${label} has no external or data URL`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function validateMarkdownLinks(relativePath) {
|
|
54
|
+
const documentPath = resolve(pluginRoot, relativePath);
|
|
55
|
+
const markdown = readFileSync(documentPath, "utf8");
|
|
56
|
+
for (const match of markdown.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
|
|
57
|
+
const target = match[1];
|
|
58
|
+
if (/^(?:https:\/\/|mailto:|#)/.test(target)) continue;
|
|
59
|
+
const withoutFragment = target.split("#")[0];
|
|
60
|
+
const resolved = resolve(dirname(documentPath), withoutFragment);
|
|
61
|
+
assert(resolved.startsWith(`${pluginRoot}${sep}`), `Markdown link remains in Plugin root: ${relativePath} -> ${target}`);
|
|
62
|
+
assert(existsSync(resolved), `Markdown link target exists: ${relativePath} -> ${target}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
assert(candidate.submissionType === "skills_only", "candidate is Skills-only");
|
|
67
|
+
assert(candidate.status === "local_alpha2_source_candidate", "candidate status is local alpha.2 source preparation");
|
|
68
|
+
assert(candidate.approval.status === "not_granted", "submission approval is not granted");
|
|
69
|
+
assert(candidate.plugin.id === manifest.name, "candidate and manifest Plugin identity match");
|
|
70
|
+
assert(candidate.plugin.version === manifest.version, "candidate and manifest version match");
|
|
71
|
+
assert(candidate.plugin.version === "0.1.0-alpha.2", "candidate version is alpha.2");
|
|
72
|
+
assert(packageJson.version === candidate.plugin.version, "optional scoped package version matches candidate");
|
|
73
|
+
assert(candidate.plugin.mcpServers.length === 0, "candidate has no MCP server requirement");
|
|
74
|
+
assert(candidate.plugin.authentication === "none", "candidate has no authentication requirement");
|
|
75
|
+
assert(candidate.plugin.coreRequirements.projectPackageJson === false, "Core requires no project package.json");
|
|
76
|
+
assert(candidate.plugin.coreRequirements.projectNodeModules === false, "Core requires no project node_modules");
|
|
77
|
+
assert(candidate.plugin.coreRequirements.npmCli === "optional", "npm CLI is optional");
|
|
78
|
+
|
|
79
|
+
for (const skill of candidate.plugin.skills) {
|
|
80
|
+
assert(existsSync(resolve(pluginRoot, "skills", skill, "SKILL.md")), `declared Skill exists: ${skill}`);
|
|
81
|
+
}
|
|
82
|
+
assert(new Set(candidate.plugin.skills).size === candidate.plugin.skills.length, "declared Skill identities are unique");
|
|
83
|
+
|
|
84
|
+
validateAsset("composerIcon", manifest.interface.composerIcon);
|
|
85
|
+
validateAsset("logo", manifest.interface.logo);
|
|
86
|
+
assert(manifest.interface.composerIcon === candidate.listing.composerIcon.value, "composer icon candidate matches manifest");
|
|
87
|
+
assert(manifest.interface.logo === candidate.listing.logo.value, "logo candidate matches manifest");
|
|
88
|
+
|
|
89
|
+
assert(candidate.listing.category.state === "confirmed_by_user" && candidate.listing.category.value === "Productivity", "Productivity category is user-confirmed");
|
|
90
|
+
assert(candidate.listing.supportUrl.state === "confirmed_by_user" && candidate.listing.supportUrl.value === "https://github.com/echopath-labs/forgerail/issues", "GitHub Issues support URL is user-confirmed");
|
|
91
|
+
assert(candidate.listing.privacyPolicyUrl.state === "confirmed_by_user" && candidate.listing.privacyPolicyUrl.value.endsWith("/PRIVACY.md"), "Privacy URL is user-confirmed and repository-owned");
|
|
92
|
+
assert(candidate.listing.termsOfServiceUrl.state === "confirmed_by_user" && candidate.listing.termsOfServiceUrl.value.endsWith("/TERMS.md"), "Terms URL is user-confirmed and repository-owned");
|
|
93
|
+
for (const field of ["websiteUrl", "supportUrl", "privacyPolicyUrl", "termsOfServiceUrl"]) {
|
|
94
|
+
const url = new URL(candidate.listing[field].value);
|
|
95
|
+
assert(url.protocol === "https:" && url.hostname === "github.com", `listing URL is confined to reviewed GitHub HTTPS origin: ${field}`);
|
|
96
|
+
}
|
|
97
|
+
assert(candidate.availability.intent.state === "confirmed_by_user" && candidate.availability.intent.value === "all_platform_supported_regions", "all-platform-supported-regions intent is user-confirmed");
|
|
98
|
+
assert(candidate.availability.portalEnumeration.state === "pending_confirmation" && candidate.availability.portalEnumeration.values.length === 0, "portal region enumeration remains pending without an invented country list");
|
|
99
|
+
assert(candidate.releaseNotes.state === "candidate" && candidate.releaseNotes.path === "./directory/release-notes-alpha2.md", "alpha.2 release notes path is explicit");
|
|
100
|
+
assert(existsSync(resolve(pluginRoot, candidate.releaseNotes.path)), "alpha.2 release notes file exists");
|
|
101
|
+
|
|
102
|
+
const privacy = readFileSync(resolve(pluginRoot, "PRIVACY.md"), "utf8");
|
|
103
|
+
const terms = readFileSync(resolve(pluginRoot, "TERMS.md"), "utf8");
|
|
104
|
+
for (const phrase of ["Skills-only Agent Plugin", "does not operate its own server", "do not themselves collect, transmit, or store telemetry", "host Agent or platform", "external Capability Packs"]) {
|
|
105
|
+
assert(privacy.includes(phrase), `Privacy notice contains required boundary: ${phrase}`);
|
|
106
|
+
}
|
|
107
|
+
for (const phrase of ["Apache License 2.0", "without warranties", "You are responsible", "external actions", "does not provide legal advice", "service-level agreement"]) {
|
|
108
|
+
assert(terms.includes(phrase), `Terms contain required boundary: ${phrase}`);
|
|
109
|
+
}
|
|
110
|
+
for (const path of ["PRIVACY.md", "TERMS.md", "README.md", "README.zh-CN.md", "docs/installation.md", "docs/installation.zh-CN.md", "docs/release-alpha2.md", "docs/release-alpha2.zh-CN.md"]) {
|
|
111
|
+
validateMarkdownLinks(path);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const requiredGates = [
|
|
115
|
+
"publisher_verified_identity",
|
|
116
|
+
"apps_management_write_permission",
|
|
117
|
+
"portal_region_enumeration",
|
|
118
|
+
"portal_asset_format_acceptance",
|
|
119
|
+
"exact_submission_public_source_confirmation"
|
|
120
|
+
];
|
|
121
|
+
assert(JSON.stringify(candidate.unresolvedGates) === JSON.stringify(requiredGates), "all external submission facts remain explicit gates");
|
|
122
|
+
|
|
123
|
+
const positive = evaluations.cases.filter((item) => item.polarity === "positive");
|
|
124
|
+
const negative = evaluations.cases.filter((item) => item.polarity === "negative");
|
|
125
|
+
assert(positive.length >= 5, "evaluation set has at least five positive cases");
|
|
126
|
+
assert(negative.length >= 3, "evaluation set has at least three negative cases");
|
|
127
|
+
assert(new Set(evaluations.cases.map((item) => item.id)).size === evaluations.cases.length, "evaluation ids are unique");
|
|
128
|
+
for (const item of positive) {
|
|
129
|
+
assert(Boolean(item.prompt && item.expectedBehavior && item.expectedResultShape?.length && item.fixture), `positive case is complete: ${item.id}`);
|
|
130
|
+
}
|
|
131
|
+
for (const item of negative) {
|
|
132
|
+
assert(Boolean(item.prompt && item.expectedFallback && item.reason && item.fixture), `negative case is complete: ${item.id}`);
|
|
133
|
+
}
|
|
134
|
+
for (const skill of candidate.plugin.skills) {
|
|
135
|
+
assert(positive.some((item) => item.skill === skill), `positive cases cover Skill: ${skill}`);
|
|
136
|
+
}
|
|
137
|
+
assert(!evaluations.cases.some((item) => /\/Users\/|secret|token=/i.test(item.prompt)), "evaluation prompts contain no private paths or credential material");
|
|
138
|
+
|
|
139
|
+
assert(!existsSync(resolve(fixture, "package.json")), "fixture has no package.json");
|
|
140
|
+
assert(!existsSync(resolve(fixture, "node_modules")), "fixture has no node_modules");
|
|
141
|
+
assert(!existsSync(resolve(fixture, ".forgerail")), "fixture starts without .forgerail state");
|
|
142
|
+
const before = hashTree(fixture);
|
|
143
|
+
const diagnosis = diagnoseWorkspace(fixture);
|
|
144
|
+
const after = hashTree(fixture);
|
|
145
|
+
assert(diagnosis.mode === "read-only", "no-Node-project diagnosis is read-only");
|
|
146
|
+
assert(diagnosis.mutations.length === 0, "no-Node-project diagnosis reports no mutation");
|
|
147
|
+
assert(diagnosis.inheritedHabits.some((item) => item.type === "markdown-adr"), "no-Node-project diagnosis discovers Markdown ADR habit");
|
|
148
|
+
assert(JSON.stringify(before) === JSON.stringify(after), "no-Node-project fixture remains byte-for-byte unchanged");
|
|
149
|
+
assert(!existsSync(resolve(fixture, ".forgerail")), "diagnosis creates no .forgerail state");
|
|
150
|
+
|
|
151
|
+
process.stdout.write(`${JSON.stringify({ status: "passed", checks: checks.length, positiveCases: positive.length, negativeCases: negative.length, skills: candidate.plugin.skills, fixture: "scripts/fixtures/workspaces/no-node-project", mutations: [] }, null, 2)}\n`);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architecture-convergence-audit
|
|
3
|
+
description: Use when explicitly asked to find duplicate capability ownership, parallel state or recovery paths, premature abstractions, or the smallest remaining implementation boundary. Produces a read-only evidence-backed convergence audit; do not use for general workspace discovery or recurring health review.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Architecture Convergence Audit
|
|
7
|
+
|
|
8
|
+
Run independently when applicable. ForgeRail Core does not need to run first.
|
|
9
|
+
Default to Analyze First, read-only operation, and `medium` risk.
|
|
10
|
+
|
|
11
|
+
## Audit
|
|
12
|
+
|
|
13
|
+
1. Bind the request to a reproducible baseline and state what freshness remains unverified.
|
|
14
|
+
2. Map each requested outcome as `product invariant -> authoritative owner -> current implementation -> callers -> persistence/lifecycle -> validation evidence`.
|
|
15
|
+
3. Trace suspected duplicate identity fences, state machines, parsers, adapters, compatibility paths, workers, recovery paths, schemas, and implementation-shaped tests to their durable fact owner and current consumer.
|
|
16
|
+
4. Apply the retention burden: require accepted behavior or a real failure/concurrency scenario, why the existing owner is insufficient, the durable fact owner, a current or committed consumer, and a validation method.
|
|
17
|
+
5. Separate confirmed redundancy from inference. Incomplete evidence returns an exact verification need, not a deletion claim.
|
|
18
|
+
6. Return the smallest remaining independently useful vertical slices and a deletion-first consolidation plan that preserves required invariants and failure coverage.
|
|
19
|
+
|
|
20
|
+
Do not infer redundancy from diff size, file count, test count, interface thinness,
|
|
21
|
+
or abstraction count. Read `references/audit-contract.md` for ownership routing,
|
|
22
|
+
required output, negative constraints, and acceptance rules.
|
|
23
|
+
|
|
24
|
+
## Boundaries
|
|
25
|
+
|
|
26
|
+
- Workspace Diagnosis discovers sources, habits, owner boundaries, commands, and dependency availability.
|
|
27
|
+
- Workspace Health owns recurring stale, drift, orphan, debt, and recovery signals.
|
|
28
|
+
- ForgeRail Core owns deterministic identity, authority, topology, revision, and Receipt invariants.
|
|
29
|
+
- This Pack owns only bounded capability-owner duplication analysis and the convergence plan.
|
|
30
|
+
- Project facts come from the existing Governance Source Inventory, Effective Profile, and Task Envelope. Do not create another Profile, adoption lifecycle, task ledger, Receipt, `.forgerail/` state, or writeback path.
|
|
31
|
+
- Do not edit, delete, merge, release, deploy, publish, change lifecycle state, or create an external issue. The template in `assets/github-issue-template.md` is output material only and requires exact external-write authorization before use.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "Architecture Convergence Audit"
|
|
3
|
+
short_description: "Trace capability owners and minimize duplicate architecture."
|
|
4
|
+
brand_color: "#B45309"
|
|
5
|
+
default_prompt: "Use $architecture-convergence-audit to identify existing owners and the smallest remaining implementation without changing the workspace."
|
|
6
|
+
policy:
|
|
7
|
+
allow_implicit_invocation: true
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## Architecture convergence candidate
|
|
2
|
+
|
|
3
|
+
Baseline and freshness:
|
|
4
|
+
|
|
5
|
+
Requested outcome:
|
|
6
|
+
|
|
7
|
+
Authoritative owner and current implementation:
|
|
8
|
+
|
|
9
|
+
Confirmed redundancy:
|
|
10
|
+
|
|
11
|
+
Inferred redundancy and verification needed:
|
|
12
|
+
|
|
13
|
+
Required invariants and failure coverage:
|
|
14
|
+
|
|
15
|
+
Smallest remaining implementation boundary:
|
|
16
|
+
|
|
17
|
+
Deletion or consolidation slices:
|
|
18
|
+
|
|
19
|
+
Negative constraints:
|
|
20
|
+
|
|
21
|
+
Validation and acceptance:
|
|
22
|
+
|
|
23
|
+
Owner decisions required:
|
|
24
|
+
|
|
25
|
+
This template is inert output material. Creating or changing an external issue
|
|
26
|
+
requires separate authorization for the exact repository and content.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Architecture Convergence Audit Contract
|
|
2
|
+
|
|
3
|
+
## Evidence and ownership
|
|
4
|
+
|
|
5
|
+
Start from the selected task outcome, not the shape of the proposed code. For
|
|
6
|
+
each outcome, identify:
|
|
7
|
+
|
|
8
|
+
- the accepted behavior and failure or concurrency scenario;
|
|
9
|
+
- the component that owns the durable fact;
|
|
10
|
+
- current implementation and direct callers;
|
|
11
|
+
- persistence, lifecycle, recovery, and compatibility responsibilities;
|
|
12
|
+
- local validation, provider/CI evidence, deployment, and real-consumer proof;
|
|
13
|
+
- freshness and any exact evidence still required.
|
|
14
|
+
|
|
15
|
+
An interface that owns authorization, transactionality, substitution,
|
|
16
|
+
anti-corruption, or failure isolation is not redundant merely because it is
|
|
17
|
+
thin. A large cohesive slice is not overbuilt merely because it spans many
|
|
18
|
+
files.
|
|
19
|
+
|
|
20
|
+
## Duplication tests
|
|
21
|
+
|
|
22
|
+
Investigate when evidence indicates two owners for the same:
|
|
23
|
+
|
|
24
|
+
- canonical identity or normalization boundary;
|
|
25
|
+
- durable state transition or lifecycle;
|
|
26
|
+
- parser or source-of-truth interpretation;
|
|
27
|
+
- compatibility or version negotiation path;
|
|
28
|
+
- recovery, replay, rollback, or resume mechanism;
|
|
29
|
+
- background worker coordination responsibility;
|
|
30
|
+
- schema, projection, or generated-contract authority.
|
|
31
|
+
|
|
32
|
+
Classify a finding as `confirmed` only when ownership and behavior are traced.
|
|
33
|
+
Otherwise use `inferred` and name the smallest verification that can resolve it.
|
|
34
|
+
|
|
35
|
+
## Required output
|
|
36
|
+
|
|
37
|
+
Return:
|
|
38
|
+
|
|
39
|
+
1. baseline, freshness, allowed scope, and explicit non-mutations;
|
|
40
|
+
2. already-owned capability map;
|
|
41
|
+
3. confirmed redundancy and inferred redundancy in separate sections;
|
|
42
|
+
4. invariants and failure coverage that must remain;
|
|
43
|
+
5. smallest remaining implementation boundary;
|
|
44
|
+
6. deletion, consolidation, or deferral candidates;
|
|
45
|
+
7. negative constraints and prohibited shortcuts;
|
|
46
|
+
8. independently verifiable vertical slices;
|
|
47
|
+
9. validation requirements and observable acceptance conditions;
|
|
48
|
+
10. owner decisions or confirmations still required.
|
|
49
|
+
|
|
50
|
+
Prefer deletion or consolidation before introducing a replacement abstraction.
|
|
51
|
+
Tests that only encode an obsolete duplicate implementation may be removed,
|
|
52
|
+
while tests that prove user-visible behavior, security, concurrency, data, or
|
|
53
|
+
operational invariants remain.
|
|
54
|
+
|
|
55
|
+
## Routing and stopping conditions
|
|
56
|
+
|
|
57
|
+
- Route source discovery to Workspace Diagnosis.
|
|
58
|
+
- Route recurring stale or drift assessment to Workspace Health.
|
|
59
|
+
- Route deterministic transition decisions to ForgeRail Core.
|
|
60
|
+
- Stop with inference when callers, durable fact ownership, or current behavior cannot be verified.
|
|
61
|
+
- Do not block an otherwise safe functional path solely for incomplete convergence evidence.
|
|
62
|
+
- Any mutation or external tracker operation requires a separate exact target, content, authority, and validation boundary.
|
|
@@ -12,6 +12,21 @@ Review the smallest useful evidence for:
|
|
|
12
12
|
- release or production rules mixed into generic workflow guidance;
|
|
13
13
|
- hidden durable mutation without human approval.
|
|
14
14
|
|
|
15
|
+
Also report these as recurring Health signals when bounded evidence exists:
|
|
16
|
+
|
|
17
|
+
- stale or unclaimed worktrees and worktrees whose recovery owner is unknown;
|
|
18
|
+
- active specification records that conflict with delivered behavior or remain active after completion;
|
|
19
|
+
- stale governance links, superseded ADR references, and generated artifacts that drift from their declared source;
|
|
20
|
+
- declared CI topology that differs from provider-observed topology, while keeping unavailable provider evidence unavailable;
|
|
21
|
+
- compatibility, test, or abstraction growth whose current consumer and recovery value are unclear;
|
|
22
|
+
- runner qualification evidence made stale by a relevant configuration or trust-boundary change.
|
|
23
|
+
|
|
24
|
+
Health observes recurring drift, debt, orphaning, and recovery cost. It does not
|
|
25
|
+
discover the workspace from scratch, mint authority, evaluate a protected
|
|
26
|
+
transition, or decide that an implementation layer is redundant. Route those
|
|
27
|
+
questions to Workspace Diagnosis, ForgeRail Core, or an explicitly invoked
|
|
28
|
+
architecture convergence audit respectively.
|
|
29
|
+
|
|
15
30
|
Prefer categorical status. Recommendations are:
|
|
16
31
|
|
|
17
32
|
- `P0`: blocks high-risk work or safe recovery;
|