@nimiplatform/nimi-coding 0.2.5 → 0.2.7
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/CHANGELOG.md +23 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/adapters/oh-my-codex/README.md +1 -1
- package/cli/commands/admit-high-risk-decision.mjs +4 -4
- package/cli/constants.mjs +1 -1
- package/cli/help.mjs +1 -1
- package/cli/lib/audit-sweep-runtime/admissions.mjs +118 -0
- package/cli/lib/audit-sweep-runtime/evidence-assignment.mjs +3 -0
- package/cli/lib/audit-sweep-runtime/inventory-spec-chunks.mjs +134 -2
- package/cli/lib/audit-sweep-runtime/inventory.mjs +33 -1
- package/cli/lib/audit-sweep-runtime/validators.mjs +34 -0
- package/cli/lib/closeout.mjs +0 -14
- package/cli/lib/entrypoints.mjs +1 -1
- package/cli/lib/high-risk-admission.mjs +68 -47
- package/cli/lib/internal/contracts-parse-high-risk.mjs +1 -1
- package/cli/lib/internal/doctor-finalize.mjs +1 -28
- package/cli/lib/internal/doctor-format.mjs +0 -1
- package/cli/lib/internal/surface-taxonomy-validators.mjs +0 -34
- package/cli/lib/internal/validators-spec-helpers.mjs +0 -1
- package/config/bootstrap.yaml +1 -1
- package/config/spec-generation-inputs.yaml +1 -1
- package/contracts/audit-chunk.schema.yaml +13 -0
- package/contracts/audit-plan.schema.yaml +15 -0
- package/contracts/high-risk-admission.schema.yaml +4 -4
- package/contracts/migration-inventory.schema.yaml +0 -2
- package/contracts/placement-contract.schema.yaml +1 -8
- package/contracts/shared-enums.yaml +0 -1
- package/contracts/spec-generation-inputs.schema.yaml +2 -2
- package/contracts/surface-taxonomy.schema.yaml +0 -11
- package/contracts/workflow-consumer.schema.yaml +0 -2
- package/methodology/spec-target-truth-profile.yaml +0 -7
- package/package.json +1 -1
- package/spec/_meta/command-gating-matrix.yaml +2 -2
- package/spec/product-scope.yaml +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ All notable changes to `@nimiplatform/nimi-coding` are tracked here.
|
|
|
4
4
|
|
|
5
5
|
This project follows semantic versioning for published npm releases.
|
|
6
6
|
|
|
7
|
+
## 0.2.7
|
|
8
|
+
|
|
9
|
+
- Added delegated projection admissions for spec-authority sweeps so a host
|
|
10
|
+
`.nimi/spec/**` subtree projected from a parent or external source authority
|
|
11
|
+
can audit host-local projection evidence while delegating source-owned
|
|
12
|
+
implementation refs through an explicit boundary.
|
|
13
|
+
- Kept delegated projections as audit modeling only: the CLI records and
|
|
14
|
+
validates source-authority boundaries, but does not read, sync, rewrite, or
|
|
15
|
+
mutate parent/external source repositories.
|
|
16
|
+
- Ignored npm package/import specifiers and explicit `./` relative refs when
|
|
17
|
+
deriving declared evidence targets, so package subpaths and YAML fragment refs
|
|
18
|
+
do not get promoted into project-local evidence paths.
|
|
19
|
+
|
|
20
|
+
## 0.2.6
|
|
21
|
+
|
|
22
|
+
- Hard-cut high-risk admission records out of active `.nimi/spec/**`
|
|
23
|
+
authority. Admission records are now local-only evidence under
|
|
24
|
+
`.nimi/local/high-risk-admissions.yaml`; product authority must live in
|
|
25
|
+
domain spec files.
|
|
26
|
+
- Removed the `product_admission_registry` surface class and changed
|
|
27
|
+
`admit-high-risk-decision` to write local evidence through `--write-local`
|
|
28
|
+
instead of writing canonical spec truth.
|
|
29
|
+
|
|
7
30
|
## 0.2.5
|
|
8
31
|
|
|
9
32
|
- Fixed the `cli_version` field in `config/bootstrap.yaml` drifting away from
|
package/README.md
CHANGED
|
@@ -314,7 +314,7 @@ repository itself keeps the package-owned source directly under
|
|
|
314
314
|
|
|
315
315
|
```bash
|
|
316
316
|
pnpm install
|
|
317
|
-
pnpm test # runs the node:test suite
|
|
317
|
+
pnpm test # runs the node:test suite
|
|
318
318
|
pnpm check:pack # npm pack --dry-run
|
|
319
319
|
pnpm check:ci # test + pack + CLI help/version smoke
|
|
320
320
|
```
|
package/README.zh-CN.md
CHANGED
|
@@ -256,7 +256,7 @@ Nimi Coding 坐在你已经用的 AI host *底下*。它是让 AI 做完的工
|
|
|
256
256
|
|
|
257
257
|
```bash
|
|
258
258
|
pnpm install
|
|
259
|
-
pnpm test # 跑 node:test
|
|
259
|
+
pnpm test # 跑 node:test 套件
|
|
260
260
|
pnpm check:pack # npm pack --dry-run
|
|
261
261
|
pnpm check:ci # test + pack + CLI help/version smoke
|
|
262
262
|
```
|
|
@@ -69,7 +69,7 @@ named host-profile recognition, and explicit manager-owned admission, but
|
|
|
69
69
|
final high-risk completion should still stay bounded by `nimicoding`
|
|
70
70
|
validators, explicit local closeout/decision/ingest/review surfaces, and
|
|
71
71
|
manual manager-side admission rather than automatic semantic promotion. The
|
|
72
|
-
|
|
72
|
+
local admission evidence target is now also shape-validated by a package-owned
|
|
73
73
|
admission schema contract before `nimicoding` accepts it as semantic truth.
|
|
74
74
|
|
|
75
75
|
## Recommended First-User Flow
|
|
@@ -10,7 +10,7 @@ function parseAdmitHighRiskDecisionOptions(args) {
|
|
|
10
10
|
fromPath: null,
|
|
11
11
|
admittedAt: null,
|
|
12
12
|
json: false,
|
|
13
|
-
|
|
13
|
+
writeLocal: false,
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -21,8 +21,8 @@ function parseAdmitHighRiskDecisionOptions(args) {
|
|
|
21
21
|
continue;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if (arg === "--write-
|
|
25
|
-
options.
|
|
24
|
+
if (arg === "--write-local") {
|
|
25
|
+
options.writeLocal = true;
|
|
26
26
|
continue;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -92,7 +92,7 @@ export async function runAdmitHighRiskDecision(args) {
|
|
|
92
92
|
return payload.exitCode;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
if (payload.ok && parsed.options.
|
|
95
|
+
if (payload.ok && parsed.options.writeLocal) {
|
|
96
96
|
await writeHighRiskAdmission(process.cwd(), payload);
|
|
97
97
|
}
|
|
98
98
|
|
package/cli/constants.mjs
CHANGED
package/cli/help.mjs
CHANGED
|
@@ -71,7 +71,7 @@ export function helpText() {
|
|
|
71
71
|
` ${styleCommand("nimicoding sweep design finalize --run-id <id> [--json]")}`,
|
|
72
72
|
` ${styleCommand("nimicoding sweep design wave-plan --run-id <id> --topic-id <id> [--json]")}`,
|
|
73
73
|
` ${styleCommand("nimicoding sweep design fix-topic --run-id <id> [--slug <slug>] [--title <title>] [--admit-first-wave|--admit-wave-id <id>] [--json]")}`,
|
|
74
|
-
` ${styleCommand("nimicoding admit-high-risk-decision --from <json> --admitted-at <iso8601> [--json] [--write-
|
|
74
|
+
` ${styleCommand("nimicoding admit-high-risk-decision --from <json> --admitted-at <iso8601> [--json] [--write-local]")}`,
|
|
75
75
|
` ${styleCommand("nimicoding closeout --skill <skill-id> --outcome <completed|blocked|failed> --verified-at <iso8601>")}`,
|
|
76
76
|
` ${styleCommand("nimicoding closeout --skill <skill-id> --outcome <completed|blocked|failed> --verified-at <iso8601> --json")}`,
|
|
77
77
|
` ${styleCommand("nimicoding closeout --skill <skill-id> --outcome <completed|blocked|failed> --verified-at <iso8601> --write-local")}`,
|
|
@@ -9,6 +9,7 @@ import { pathExists } from "../fs-helpers.mjs";
|
|
|
9
9
|
export const AUDIT_SWEEP_PROJECT_CONFIG_REF = ".nimi/config/audit-sweep.yaml";
|
|
10
10
|
export const APP_SLICE_ADMISSION_REF = ".nimi/spec/platform/kernel/tables/app-slice-admissions.yaml";
|
|
11
11
|
const AUDIT_EVIDENCE_ROOT_TABLE_BASENAME = "audit-evidence-roots.yaml";
|
|
12
|
+
const DELEGATED_PROJECTION_ADMISSION_BASENAME = "delegated-projection-admissions.yaml";
|
|
12
13
|
const PACKAGE_AUTHORITY_ADMISSION_BASENAME = "package-authority-admissions.yaml";
|
|
13
14
|
|
|
14
15
|
export function refInsideRoot(fileRef, rootRef) {
|
|
@@ -23,6 +24,32 @@ function safeProjectRef(value) {
|
|
|
23
24
|
&& !value.split("/").includes("..");
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
function normalizeProjectRef(value) {
|
|
28
|
+
const normalized = String(value ?? "").trim().replace(/\\/g, "/");
|
|
29
|
+
if (/^[a-z][a-z0-9+.-]*:\/\/$/iu.test(normalized)) {
|
|
30
|
+
return normalized;
|
|
31
|
+
}
|
|
32
|
+
return normalized.replace(/\/$/, "");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function activePosture(row) {
|
|
36
|
+
return String(row?.status ?? row?.admission_posture ?? "").trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function safeProjectionSourceRef(value) {
|
|
40
|
+
const normalized = normalizeProjectRef(value);
|
|
41
|
+
return normalized.startsWith("parent://")
|
|
42
|
+
|| normalized.startsWith("package://")
|
|
43
|
+
|| safeProjectRef(normalized);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function safeRelativePath(value) {
|
|
47
|
+
const normalized = normalizeProjectRef(value);
|
|
48
|
+
return normalized.length > 0
|
|
49
|
+
&& !path.isAbsolute(normalized)
|
|
50
|
+
&& !normalized.split("/").includes("..");
|
|
51
|
+
}
|
|
52
|
+
|
|
26
53
|
export async function loadAuditSweepProjectConfig(projectRoot) {
|
|
27
54
|
const configPath = artifactPath(projectRoot, AUDIT_SWEEP_PROJECT_CONFIG_REF);
|
|
28
55
|
const info = await pathExists(configPath);
|
|
@@ -241,6 +268,97 @@ export async function loadAuditEvidenceRootAdmissions(projectRoot, listGitFiles,
|
|
|
241
268
|
return { ok: true, tableRefs, admissions };
|
|
242
269
|
}
|
|
243
270
|
|
|
271
|
+
export async function loadDelegatedProjectionAdmissions(projectRoot, listGitFiles, listFallbackFiles) {
|
|
272
|
+
const tableRefs = await listSpecTableRefs(projectRoot, DELEGATED_PROJECTION_ADMISSION_BASENAME, listGitFiles, listFallbackFiles);
|
|
273
|
+
if (!tableRefs) {
|
|
274
|
+
return { ok: true, tableRefs: [], admissions: [] };
|
|
275
|
+
}
|
|
276
|
+
const admissions = [];
|
|
277
|
+
const seenIds = new Set();
|
|
278
|
+
|
|
279
|
+
for (const tableRef of tableRefs) {
|
|
280
|
+
let parsed;
|
|
281
|
+
try {
|
|
282
|
+
parsed = YAML.parse(await readFile(artifactPath(projectRoot, tableRef), "utf8"));
|
|
283
|
+
} catch (error) {
|
|
284
|
+
return {
|
|
285
|
+
ok: false,
|
|
286
|
+
error: `nimicoding sweep audit refused: ${tableRef} must contain valid YAML (${error.message}).\n`,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const rows = Array.isArray(parsed?.admissions) ? parsed.admissions : null;
|
|
290
|
+
if (!rows) {
|
|
291
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} must declare admissions as an array.\n` };
|
|
292
|
+
}
|
|
293
|
+
for (const row of rows) {
|
|
294
|
+
const id = String(row?.id ?? "").trim();
|
|
295
|
+
const posture = activePosture(row);
|
|
296
|
+
const ownerDomain = String(row?.owner_domain ?? "").trim();
|
|
297
|
+
const authorityRoot = normalizeProjectRef(row?.authority_root);
|
|
298
|
+
const sourceAuthorityRoot = normalizeProjectRef(row?.source_authority_root);
|
|
299
|
+
const localProjectionEvidenceRoots = Array.isArray(row?.local_projection_evidence_roots)
|
|
300
|
+
? row.local_projection_evidence_roots.map(normalizeProjectRef).filter(Boolean)
|
|
301
|
+
: null;
|
|
302
|
+
const delegatedEvidenceRoots = Array.isArray(row?.delegated_evidence_roots)
|
|
303
|
+
? row.delegated_evidence_roots.map(normalizeProjectRef).filter(Boolean)
|
|
304
|
+
: null;
|
|
305
|
+
const delegatedDeclaredEvidencePrefixes = Array.isArray(row?.delegated_declared_evidence_prefixes)
|
|
306
|
+
? row.delegated_declared_evidence_prefixes.map(normalizeProjectRef).filter(Boolean)
|
|
307
|
+
: [];
|
|
308
|
+
const hostOwnedRelativePaths = Array.isArray(row?.host_owned_relative_paths)
|
|
309
|
+
? row.host_owned_relative_paths.map(normalizeProjectRef).filter(Boolean)
|
|
310
|
+
: [];
|
|
311
|
+
const requiredVerificationCommands = Array.isArray(row?.required_verification_commands)
|
|
312
|
+
? row.required_verification_commands.map((entry) => String(entry ?? "").trim()).filter(Boolean)
|
|
313
|
+
: [];
|
|
314
|
+
const admissionKey = `${tableRef}#${id}`;
|
|
315
|
+
if (!id || seenIds.has(admissionKey)) {
|
|
316
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} has missing or duplicate delegated projection id.\n` };
|
|
317
|
+
}
|
|
318
|
+
seenIds.add(admissionKey);
|
|
319
|
+
if (posture !== "active") {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (!ownerDomain || !safeProjectRef(authorityRoot) || !authorityRoot.startsWith(".nimi/spec/")) {
|
|
323
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} ${id} has invalid owner_domain or authority_root.\n` };
|
|
324
|
+
}
|
|
325
|
+
if (!safeProjectionSourceRef(sourceAuthorityRoot)) {
|
|
326
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} ${id} has invalid source_authority_root.\n` };
|
|
327
|
+
}
|
|
328
|
+
if (!localProjectionEvidenceRoots?.length || !localProjectionEvidenceRoots.every((rootRef) => safeProjectRef(rootRef) && !rootRef.startsWith(".nimi/spec/"))) {
|
|
329
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} ${id} has invalid local_projection_evidence_roots.\n` };
|
|
330
|
+
}
|
|
331
|
+
if (!delegatedEvidenceRoots?.length || !delegatedEvidenceRoots.every(safeProjectionSourceRef)) {
|
|
332
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} ${id} has invalid delegated_evidence_roots.\n` };
|
|
333
|
+
}
|
|
334
|
+
if (delegatedDeclaredEvidencePrefixes.length === 0 || !delegatedDeclaredEvidencePrefixes.every(safeRelativePath)) {
|
|
335
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} ${id} must declare delegated_declared_evidence_prefixes.\n` };
|
|
336
|
+
}
|
|
337
|
+
if (!hostOwnedRelativePaths.every(safeRelativePath)) {
|
|
338
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} ${id} has invalid host_owned_relative_paths.\n` };
|
|
339
|
+
}
|
|
340
|
+
if (!requiredVerificationCommands.every((command) => command.length > 0)) {
|
|
341
|
+
return { ok: false, error: `nimicoding sweep audit refused: ${tableRef} ${id} has invalid required_verification_commands.\n` };
|
|
342
|
+
}
|
|
343
|
+
admissions.push({
|
|
344
|
+
id,
|
|
345
|
+
owner_domain: ownerDomain,
|
|
346
|
+
status: posture,
|
|
347
|
+
authority_root: authorityRoot,
|
|
348
|
+
source_authority_root: sourceAuthorityRoot,
|
|
349
|
+
local_projection_evidence_roots: localProjectionEvidenceRoots,
|
|
350
|
+
delegated_evidence_roots: delegatedEvidenceRoots,
|
|
351
|
+
delegated_declared_evidence_prefixes: delegatedDeclaredEvidencePrefixes,
|
|
352
|
+
host_owned_relative_paths: hostOwnedRelativePaths,
|
|
353
|
+
required_verification_commands: requiredVerificationCommands,
|
|
354
|
+
admission_ref: admissionKey,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return { ok: true, tableRefs, admissions };
|
|
360
|
+
}
|
|
361
|
+
|
|
244
362
|
export async function loadPackageAuthorityAdmissions(projectRoot, listGitFiles, listFallbackFiles) {
|
|
245
363
|
const tableRefs = await listSpecTableRefs(projectRoot, PACKAGE_AUTHORITY_ADMISSION_BASENAME, listGitFiles, listFallbackFiles);
|
|
246
364
|
if (!tableRefs) {
|
|
@@ -67,6 +67,9 @@ function chunkAcceptsEvidenceFile(chunk, fileRef) {
|
|
|
67
67
|
if (surface === "spec-generation-audit" || surface === "high-risk-admissions" || surface === "package-meta" || surface === "package-root") {
|
|
68
68
|
return true;
|
|
69
69
|
}
|
|
70
|
+
if (surface === "delegated-projection") {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
70
73
|
return false;
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -46,11 +46,17 @@ function looksLikeSpecAuthorityRelativeRef(normalized) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function normalizeDeclaredEvidenceRef(value) {
|
|
49
|
-
const
|
|
49
|
+
const raw = String(value ?? "")
|
|
50
50
|
.trim()
|
|
51
51
|
.replace(/\\/g, "/")
|
|
52
|
-
.replace(/^\.\//, "")
|
|
53
52
|
.replace(/[),.;:]+$/u, "");
|
|
53
|
+
if (!raw || raw.startsWith("../") || raw.includes("/../") || raw.startsWith("http:") || raw.startsWith("https:")) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (raw.startsWith("@") || raw.startsWith("./")) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const normalized = raw.replace(/^\.\//, "");
|
|
54
60
|
if (!normalized || normalized.startsWith("../") || normalized.includes("/../") || normalized.startsWith("http:") || normalized.startsWith("https:")) {
|
|
55
61
|
return null;
|
|
56
62
|
}
|
|
@@ -102,6 +108,37 @@ function candidateEvidenceRefsForDeclaredRef(declaredRef, evidenceRoots) {
|
|
|
102
108
|
return [...new Set(candidates)].sort();
|
|
103
109
|
}
|
|
104
110
|
|
|
111
|
+
function normalizedRef(value) {
|
|
112
|
+
return String(value ?? "").replace(/\\/g, "/").replace(/\/$/, "");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function refInsideRoot(fileRef, rootRef) {
|
|
116
|
+
const root = normalizedRef(rootRef);
|
|
117
|
+
return Boolean(root) && (fileRef === root || fileRef.startsWith(`${root}/`));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function delegatedProjectionAdmissionForFile(fileRef, admissions = []) {
|
|
121
|
+
return admissions.find((admission) => {
|
|
122
|
+
if (!refInsideRoot(fileRef, admission.authority_root)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const relative = fileRef === admission.authority_root
|
|
126
|
+
? ""
|
|
127
|
+
: fileRef.slice(admission.authority_root.length + 1);
|
|
128
|
+
return !(admission.host_owned_relative_paths ?? []).some((hostOwnedPath) => (
|
|
129
|
+
relative === hostOwnedPath || relative.startsWith(`${hostOwnedPath}/`)
|
|
130
|
+
));
|
|
131
|
+
}) ?? null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function declaredRefDelegated(ref, prefixes = []) {
|
|
135
|
+
const normalized = normalizedRef(ref);
|
|
136
|
+
return prefixes.some((prefix) => {
|
|
137
|
+
const normalizedPrefix = normalizedRef(prefix);
|
|
138
|
+
return normalizedPrefix && (normalized === normalizedPrefix || normalized.startsWith(`${normalizedPrefix}/`));
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
105
142
|
function extractDeclaredEvidenceRefs(text) {
|
|
106
143
|
const refs = [];
|
|
107
144
|
for (const match of String(text ?? "").matchAll(DECLARED_EVIDENCE_REF_PATTERN)) {
|
|
@@ -147,6 +184,74 @@ function extractModuleMapRefs(markdownText) {
|
|
|
147
184
|
return [...new Set(refs)].sort();
|
|
148
185
|
}
|
|
149
186
|
|
|
187
|
+
function buildDelegatedProjectionChunk({
|
|
188
|
+
admission,
|
|
189
|
+
entries,
|
|
190
|
+
chunkIndex,
|
|
191
|
+
criteria,
|
|
192
|
+
authorityTextByRef,
|
|
193
|
+
}) {
|
|
194
|
+
const authorityRefs = entries.map((entry) => entry.file_ref).sort();
|
|
195
|
+
const authorityText = authorityRefs
|
|
196
|
+
.map((authorityRef) => authorityTextByRef?.get(authorityRef) ?? "")
|
|
197
|
+
.join("\n");
|
|
198
|
+
const declaredEvidenceRefs = extractDeclaredEvidenceRefs(authorityText);
|
|
199
|
+
const delegatedDeclaredEvidenceRefs = declaredEvidenceRefs
|
|
200
|
+
.filter((ref) => declaredRefDelegated(ref, admission.delegated_declared_evidence_prefixes))
|
|
201
|
+
.sort();
|
|
202
|
+
const localDeclaredEvidenceRefs = declaredEvidenceRefs
|
|
203
|
+
.filter((ref) => !declaredRefDelegated(ref, admission.delegated_declared_evidence_prefixes))
|
|
204
|
+
.sort();
|
|
205
|
+
const evidenceRoots = [...new Set(admission.local_projection_evidence_roots ?? [])].sort();
|
|
206
|
+
const declaredEvidenceTargets = localDeclaredEvidenceRefs
|
|
207
|
+
.map((evidenceRef) => ({
|
|
208
|
+
source_path: evidenceRef,
|
|
209
|
+
candidates: candidateEvidenceRefsForDeclaredRef(evidenceRef, evidenceRoots),
|
|
210
|
+
}))
|
|
211
|
+
.filter((target) => target.candidates.length > 0);
|
|
212
|
+
return {
|
|
213
|
+
chunk_id: [
|
|
214
|
+
`chunk-${String(chunkIndex).padStart(3, "0")}`,
|
|
215
|
+
slugPart(admission.owner_domain),
|
|
216
|
+
"delegated-projection",
|
|
217
|
+
slugPart(admission.id),
|
|
218
|
+
].join("-"),
|
|
219
|
+
state: "planned",
|
|
220
|
+
owner_domain: admission.owner_domain,
|
|
221
|
+
planning_basis: "spec_authority",
|
|
222
|
+
spec_surface: "delegated-projection",
|
|
223
|
+
criteria,
|
|
224
|
+
files: authorityRefs,
|
|
225
|
+
authority_refs: authorityRefs,
|
|
226
|
+
authority_kind: "delegated_projection",
|
|
227
|
+
delegated_projection_id: admission.id,
|
|
228
|
+
admission_ref: admission.admission_ref,
|
|
229
|
+
authority_root: admission.authority_root,
|
|
230
|
+
source_authority_root: admission.source_authority_root,
|
|
231
|
+
admitted_evidence_roots: evidenceRoots,
|
|
232
|
+
delegated_evidence_roots: admission.delegated_evidence_roots,
|
|
233
|
+
delegated_declared_evidence_prefixes: admission.delegated_declared_evidence_prefixes,
|
|
234
|
+
...(delegatedDeclaredEvidenceRefs.length > 0 ? {
|
|
235
|
+
delegated_declared_evidence_refs: delegatedDeclaredEvidenceRefs,
|
|
236
|
+
declared_evidence_delegation: {
|
|
237
|
+
status: "delegated_to_source_authority",
|
|
238
|
+
delegated_ref_count: delegatedDeclaredEvidenceRefs.length,
|
|
239
|
+
source_authority_root: admission.source_authority_root,
|
|
240
|
+
delegated_evidence_roots: admission.delegated_evidence_roots,
|
|
241
|
+
},
|
|
242
|
+
} : {}),
|
|
243
|
+
...(admission.required_verification_commands?.length > 0 ? {
|
|
244
|
+
required_verification_commands: admission.required_verification_commands,
|
|
245
|
+
} : {}),
|
|
246
|
+
...(declaredEvidenceTargets.length > 0 ? {
|
|
247
|
+
declared_evidence_targets: declaredEvidenceTargets,
|
|
248
|
+
} : {}),
|
|
249
|
+
evidence_roots: evidenceRoots,
|
|
250
|
+
file_count: authorityRefs.length,
|
|
251
|
+
finding_count: 0,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
150
255
|
function candidateEvidenceRefsForModuleMapPath(modulePath, evidenceRoots) {
|
|
151
256
|
const normalized = String(modulePath ?? "").replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
152
257
|
if (!normalized || normalized.startsWith("http:") || normalized.startsWith("https:")) {
|
|
@@ -181,6 +286,16 @@ function candidateEvidenceRefsForModuleMapPath(modulePath, evidenceRoots) {
|
|
|
181
286
|
export function buildSpecChunks(includedInventory, options) {
|
|
182
287
|
const sortedEntries = [...includedInventory].sort((left, right) => left.file_ref.localeCompare(right.file_ref));
|
|
183
288
|
const includedRefs = new Set(sortedEntries.map((entry) => entry.file_ref));
|
|
289
|
+
const delegatedEntriesByAdmissionRef = new Map();
|
|
290
|
+
for (const entry of sortedEntries) {
|
|
291
|
+
const admission = delegatedProjectionAdmissionForFile(entry.file_ref, options.delegatedProjectionAdmissions);
|
|
292
|
+
if (!admission) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const entries = delegatedEntriesByAdmissionRef.get(admission.admission_ref) ?? [];
|
|
296
|
+
entries.push(entry);
|
|
297
|
+
delegatedEntriesByAdmissionRef.set(admission.admission_ref, entries);
|
|
298
|
+
}
|
|
184
299
|
const hostProjectionByHostRef = new Map();
|
|
185
300
|
const hostProjectionsByPackageRef = new Map();
|
|
186
301
|
for (const admission of options.packageAuthorityAdmissions ?? []) {
|
|
@@ -202,7 +317,24 @@ export function buildSpecChunks(includedInventory, options) {
|
|
|
202
317
|
}
|
|
203
318
|
let chunkIndex = 0;
|
|
204
319
|
const chunks = [];
|
|
320
|
+
const emittedDelegatedAdmissions = new Set();
|
|
205
321
|
for (const entry of sortedEntries) {
|
|
322
|
+
const delegatedAdmission = delegatedProjectionAdmissionForFile(entry.file_ref, options.delegatedProjectionAdmissions);
|
|
323
|
+
if (delegatedAdmission) {
|
|
324
|
+
if (emittedDelegatedAdmissions.has(delegatedAdmission.admission_ref)) {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
emittedDelegatedAdmissions.add(delegatedAdmission.admission_ref);
|
|
328
|
+
chunkIndex += 1;
|
|
329
|
+
chunks.push(buildDelegatedProjectionChunk({
|
|
330
|
+
admission: delegatedAdmission,
|
|
331
|
+
entries: delegatedEntriesByAdmissionRef.get(delegatedAdmission.admission_ref) ?? [entry],
|
|
332
|
+
chunkIndex,
|
|
333
|
+
criteria: options.criteria,
|
|
334
|
+
authorityTextByRef: options.authorityTextByRef,
|
|
335
|
+
}));
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
206
338
|
if (hostProjectionByHostRef.has(entry.file_ref)) {
|
|
207
339
|
continue;
|
|
208
340
|
}
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
loadAppSliceAdmissions,
|
|
32
32
|
loadAuditEvidenceRootAdmissions,
|
|
33
33
|
loadAuditSweepProjectConfig,
|
|
34
|
+
loadDelegatedProjectionAdmissions,
|
|
34
35
|
loadPackageAuthorityAdmissions,
|
|
35
36
|
} from "./admissions.mjs";
|
|
36
37
|
import { buildCoverageQuality } from "./coverage-quality.mjs";
|
|
@@ -441,6 +442,8 @@ export async function createAuditSweepPlan(projectRoot, options) {
|
|
|
441
442
|
let appSliceAdmissions = [];
|
|
442
443
|
let auditEvidenceRootAdmissions = [];
|
|
443
444
|
let auditEvidenceRootAdmissionRefs = [];
|
|
445
|
+
let delegatedProjectionAdmissions = [];
|
|
446
|
+
let delegatedProjectionAdmissionRefs = [];
|
|
444
447
|
let packageAuthorityAdmissions = [];
|
|
445
448
|
let packageAuthorityAdmissionRefs = [];
|
|
446
449
|
if (chunkBasis.basis === "spec") {
|
|
@@ -455,6 +458,12 @@ export async function createAuditSweepPlan(projectRoot, options) {
|
|
|
455
458
|
}
|
|
456
459
|
auditEvidenceRootAdmissions = loadedEvidenceRootAdmissions.admissions;
|
|
457
460
|
auditEvidenceRootAdmissionRefs = loadedEvidenceRootAdmissions.tableRefs;
|
|
461
|
+
const loadedDelegatedProjectionAdmissions = await loadDelegatedProjectionAdmissions(projectRoot, listGitFiles, listFallbackFiles);
|
|
462
|
+
if (!loadedDelegatedProjectionAdmissions.ok) {
|
|
463
|
+
return inputError(loadedDelegatedProjectionAdmissions.error);
|
|
464
|
+
}
|
|
465
|
+
delegatedProjectionAdmissions = loadedDelegatedProjectionAdmissions.admissions;
|
|
466
|
+
delegatedProjectionAdmissionRefs = loadedDelegatedProjectionAdmissions.tableRefs;
|
|
458
467
|
const loadedPackageAuthorityAdmissions = await loadPackageAuthorityAdmissions(projectRoot, listGitFiles, listFallbackFiles);
|
|
459
468
|
if (!loadedPackageAuthorityAdmissions.ok) {
|
|
460
469
|
return inputError(loadedPackageAuthorityAdmissions.error);
|
|
@@ -513,7 +522,7 @@ export async function createAuditSweepPlan(projectRoot, options) {
|
|
|
513
522
|
}
|
|
514
523
|
}
|
|
515
524
|
let chunks = chunkBasis.basis === "spec"
|
|
516
|
-
? buildSpecChunks(includedInventory, { criteria, targetRootRef, appSliceAdmissions, auditEvidenceRootAdmissions, packageAuthorityAdmissions, authorityTextByRef })
|
|
525
|
+
? buildSpecChunks(includedInventory, { criteria, targetRootRef, appSliceAdmissions, auditEvidenceRootAdmissions, delegatedProjectionAdmissions, packageAuthorityAdmissions, authorityTextByRef })
|
|
517
526
|
: buildFileChunks(includedInventory, { criteria, maxFilesPerChunk });
|
|
518
527
|
let evidenceInventory = [];
|
|
519
528
|
let unmappedEvidenceFiles = [];
|
|
@@ -589,6 +598,22 @@ export async function createAuditSweepPlan(projectRoot, options) {
|
|
|
589
598
|
...(chunkBasis.basis === "spec" && auditEvidenceRootAdmissionRefs.length > 0 ? {
|
|
590
599
|
audit_evidence_root_refs: auditEvidenceRootAdmissionRefs,
|
|
591
600
|
} : {}),
|
|
601
|
+
...(chunkBasis.basis === "spec" && delegatedProjectionAdmissions.length > 0 ? {
|
|
602
|
+
delegated_projection_admission_refs: delegatedProjectionAdmissionRefs,
|
|
603
|
+
delegated_projection_admissions: delegatedProjectionAdmissions.map((admission) => ({
|
|
604
|
+
id: admission.id,
|
|
605
|
+
owner_domain: admission.owner_domain,
|
|
606
|
+
status: admission.status,
|
|
607
|
+
authority_root: admission.authority_root,
|
|
608
|
+
source_authority_root: admission.source_authority_root,
|
|
609
|
+
local_projection_evidence_roots: admission.local_projection_evidence_roots,
|
|
610
|
+
delegated_evidence_roots: admission.delegated_evidence_roots,
|
|
611
|
+
delegated_declared_evidence_prefixes: admission.delegated_declared_evidence_prefixes,
|
|
612
|
+
host_owned_relative_paths: admission.host_owned_relative_paths,
|
|
613
|
+
required_verification_commands: admission.required_verification_commands,
|
|
614
|
+
admission_ref: admission.admission_ref,
|
|
615
|
+
})),
|
|
616
|
+
} : {}),
|
|
592
617
|
...(chunkBasis.basis === "spec" && packageAuthorityAdmissions.length > 0 ? {
|
|
593
618
|
package_authority_admission_refs: packageAuthorityAdmissionRefs,
|
|
594
619
|
package_authority_admissions: packageAuthorityAdmissions.map((admission) => ({
|
|
@@ -669,11 +694,18 @@ export async function createAuditSweepPlan(projectRoot, options) {
|
|
|
669
694
|
...(chunk.authority_kind ? { authority_kind: chunk.authority_kind } : {}),
|
|
670
695
|
...(chunk.app_id ? { app_id: chunk.app_id } : {}),
|
|
671
696
|
...(chunk.package_authority_id ? { package_authority_id: chunk.package_authority_id } : {}),
|
|
697
|
+
...(chunk.delegated_projection_id ? { delegated_projection_id: chunk.delegated_projection_id } : {}),
|
|
672
698
|
...(chunk.admission_ref ? { admission_ref: chunk.admission_ref } : {}),
|
|
673
699
|
...(chunk.authority_root ? { authority_root: chunk.authority_root } : {}),
|
|
700
|
+
...(chunk.source_authority_root ? { source_authority_root: chunk.source_authority_root } : {}),
|
|
674
701
|
...(chunk.evidence_root_admission_refs ? { evidence_root_admission_refs: chunk.evidence_root_admission_refs } : {}),
|
|
675
702
|
...(chunk.admitted_evidence_roots ? { admitted_evidence_roots: chunk.admitted_evidence_roots } : {}),
|
|
676
703
|
...(chunk.host_authority_projection_refs ? { host_authority_projection_refs: chunk.host_authority_projection_refs } : {}),
|
|
704
|
+
...(chunk.delegated_evidence_roots ? { delegated_evidence_roots: chunk.delegated_evidence_roots } : {}),
|
|
705
|
+
...(chunk.delegated_declared_evidence_prefixes ? { delegated_declared_evidence_prefixes: chunk.delegated_declared_evidence_prefixes } : {}),
|
|
706
|
+
...(chunk.delegated_declared_evidence_refs ? { delegated_declared_evidence_refs: chunk.delegated_declared_evidence_refs } : {}),
|
|
707
|
+
...(chunk.declared_evidence_delegation ? { declared_evidence_delegation: chunk.declared_evidence_delegation } : {}),
|
|
708
|
+
...(chunk.required_verification_commands ? { required_verification_commands: chunk.required_verification_commands } : {}),
|
|
677
709
|
...(chunk.declared_evidence_targets ? { declared_evidence_targets: chunk.declared_evidence_targets } : {}),
|
|
678
710
|
...(chunk.evidence_roots ? { evidence_roots: chunk.evidence_roots } : {}),
|
|
679
711
|
...(chunk.declared_evidence_unresolved ? { declared_evidence_unresolved: chunk.declared_evidence_unresolved } : {}),
|
|
@@ -259,6 +259,40 @@ function validatePlanShape(plan, sweepId, checks) {
|
|
|
259
259
|
));
|
|
260
260
|
});
|
|
261
261
|
check(checks, "plan_spec_package_authority_admitted", packageAuthorityChunksOk, "package-local authority chunks are admitted through .nimi/spec package authority admissions");
|
|
262
|
+
const delegatedProjectionAdmissions = Array.isArray(plan.delegated_projection_admissions) ? plan.delegated_projection_admissions : [];
|
|
263
|
+
const delegatedAdmissionByRef = new Map(delegatedProjectionAdmissions
|
|
264
|
+
.filter((entry) => isPlainObject(entry) && nonEmptyString(entry.admission_ref))
|
|
265
|
+
.map((entry) => [entry.admission_ref, entry]));
|
|
266
|
+
const delegatedProjectionChunks = plan.chunks.filter((chunk) => chunk.authority_kind === "delegated_projection");
|
|
267
|
+
const delegatedProjectionChunksOk = delegatedProjectionChunks.every((chunk) => {
|
|
268
|
+
const admission = delegatedAdmissionByRef.get(chunk.admission_ref);
|
|
269
|
+
const prefixes = Array.isArray(admission?.delegated_declared_evidence_prefixes)
|
|
270
|
+
? admission.delegated_declared_evidence_prefixes
|
|
271
|
+
: [];
|
|
272
|
+
const delegatedRefs = Array.isArray(chunk.delegated_declared_evidence_refs)
|
|
273
|
+
? chunk.delegated_declared_evidence_refs
|
|
274
|
+
: [];
|
|
275
|
+
const delegatedRefsOk = delegatedRefs.every((ref) => prefixes.some((prefix) => (
|
|
276
|
+
String(ref) === String(prefix).replace(/\/$/u, "")
|
|
277
|
+
|| String(ref).startsWith(`${String(prefix).replace(/\/$/u, "")}/`)
|
|
278
|
+
)));
|
|
279
|
+
return nonEmptyString(chunk.delegated_projection_id)
|
|
280
|
+
&& nonEmptyString(chunk.admission_ref)
|
|
281
|
+
&& nonEmptyString(chunk.authority_root)
|
|
282
|
+
&& nonEmptyString(chunk.source_authority_root)
|
|
283
|
+
&& admission
|
|
284
|
+
&& admission.id === chunk.delegated_projection_id
|
|
285
|
+
&& admission.authority_root === chunk.authority_root
|
|
286
|
+
&& admission.source_authority_root === chunk.source_authority_root
|
|
287
|
+
&& Array.isArray(admission.local_projection_evidence_roots)
|
|
288
|
+
&& sortedArrayEquals(chunk.evidence_roots, admission.local_projection_evidence_roots)
|
|
289
|
+
&& sortedArrayEquals(chunk.admitted_evidence_roots, admission.local_projection_evidence_roots)
|
|
290
|
+
&& sortedArrayEquals(chunk.delegated_evidence_roots, admission.delegated_evidence_roots)
|
|
291
|
+
&& sortedArrayEquals(chunk.delegated_declared_evidence_prefixes, admission.delegated_declared_evidence_prefixes)
|
|
292
|
+
&& delegatedRefsOk
|
|
293
|
+
&& chunk.authority_refs.every((fileRef) => String(fileRef).startsWith(`${chunk.authority_root}/`));
|
|
294
|
+
});
|
|
295
|
+
check(checks, "plan_spec_delegated_projection_authority_admitted", delegatedProjectionChunksOk, "delegated projection chunks are admitted through .nimi/spec delegated projection admissions");
|
|
262
296
|
const evidenceRootAdmissionsOk = plan.chunks.every((chunk) => (
|
|
263
297
|
!Array.isArray(chunk.evidence_root_admission_refs)
|
|
264
298
|
|| chunk.evidence_root_admission_refs.every((ref) => (
|
package/cli/lib/closeout.mjs
CHANGED
|
@@ -44,7 +44,6 @@ function translateCloseoutReason(reason) {
|
|
|
44
44
|
["Completed closeout requires declared canonical tree files to be valid", "完成 closeout 需要声明的 canonical tree 文件有效"],
|
|
45
45
|
["Completed closeout requires a valid `.nimi/local/state/spec-generation/spec-generation-audit.yaml` artifact", "完成 closeout 需要一个有效的 `.nimi/local/state/spec-generation/spec-generation-audit.yaml` 产物"],
|
|
46
46
|
["Completed doc_spec_audit closeout must compare against `.nimi/spec`", "完成 doc_spec_audit closeout 时必须对 `.nimi/spec` 进行比较"],
|
|
47
|
-
["Completed high_risk_execution closeout requires canonical admissions truth to remain `.nimi/spec/high-risk-admissions.yaml`", "完成 high_risk_execution closeout 需要 canonical admissions truth 继续落在 `.nimi/spec/high-risk-admissions.yaml`"],
|
|
48
47
|
["Completed closeout is consistent with the current canonical tree state", "completed closeout 与当前 canonical tree 状态一致"],
|
|
49
48
|
["Imported spec_reconstruction summary must match active spec-generation audit coverage", "导入的 spec_reconstruction 摘要必须与当前 spec-generation audit 覆盖情况一致"],
|
|
50
49
|
]);
|
|
@@ -395,19 +394,6 @@ function evaluateCloseoutReadiness(skillId, outcome, doctorResult, summary) {
|
|
|
395
394
|
}
|
|
396
395
|
}
|
|
397
396
|
|
|
398
|
-
if (rule.completedRequires.high_risk_admissions_truth_ref) {
|
|
399
|
-
const admissionsTruthPath = rule.completedRequires.high_risk_admissions_truth_ref;
|
|
400
|
-
const expectedAdmissionsPath = doctorResult.commandGating.entries.find(
|
|
401
|
-
(entry) => entry.command === "admit-high-risk-decision" && entry.requires?.canonical_admissions_truth,
|
|
402
|
-
)?.requires?.canonical_admissions_truth ?? ".nimi/spec/high-risk-admissions.yaml";
|
|
403
|
-
if (admissionsTruthPath !== expectedAdmissionsPath) {
|
|
404
|
-
return {
|
|
405
|
-
ok: false,
|
|
406
|
-
reason: "Completed high_risk_execution closeout requires canonical admissions truth to remain `.nimi/spec/high-risk-admissions.yaml`",
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
397
|
if (!doctorResult.ok || !doctorResult.handoffReadiness.ok) {
|
|
412
398
|
return {
|
|
413
399
|
ok: false,
|
package/cli/lib/entrypoints.mjs
CHANGED
|
@@ -18,7 +18,7 @@ function managedAgentsBlock() {
|
|
|
18
18
|
- Treat \`/.nimi/spec/**\` as the current repo-wide product authority for this project, and use Git history for retired pre-cutover authority evidence.
|
|
19
19
|
- If .nimi/spec remains bootstrap-only, use .nimi/methodology/spec-reconstruction.yaml and .nimi/config/skills.yaml to drive AI-side truth reconstruction.
|
|
20
20
|
- Treat .nimi/methodology/spec-target-truth-profile.yaml as repo-local support guidance for future governance slices, not as the canonical reconstruction completion target or a guaranteed fresh-bootstrap seed.
|
|
21
|
-
- Treat .nimi/contracts/spec-reconstruction-result.yaml, .nimi/contracts/doc-spec-audit-result.yaml, .nimi/contracts/high-risk-execution-result.yaml, and .nimi/contracts/high-risk-admission.schema.yaml as machine contracts for reconstruction, audit, local-only high-risk closeout summaries, and
|
|
21
|
+
- Treat .nimi/contracts/spec-reconstruction-result.yaml, .nimi/contracts/doc-spec-audit-result.yaml, .nimi/contracts/high-risk-execution-result.yaml, and .nimi/contracts/high-risk-admission.schema.yaml as machine contracts for reconstruction, audit, local-only high-risk closeout summaries, and local-only high-risk admission evidence.
|
|
22
22
|
- Treat .nimi/config/skill-manifest.yaml, .nimi/config/host-profile.yaml, .nimi/config/host-adapter.yaml, .nimi/config/external-execution-artifacts.yaml, .nimi/config/skill-installer.yaml, .nimi/methodology/skill-runtime.yaml, .nimi/methodology/skill-installer-result.yaml, .nimi/methodology/skill-handoff.yaml, and admitted package-owned adapter profiles under adapters/**/profile.yaml as the canonical bridge to any external AI/skill execution.
|
|
23
23
|
- Treat standalone nimicoding as boundary-complete for bootstrap, handoff, validation, projection, and explicit admission only; do not assume packaged run-kernel, provider, scheduler, notification, or automation ownership.
|
|
24
24
|
- Treat .nimi/config/installer-evidence.yaml and .nimi/methodology/skill-installer-summary-projection.yaml as the operational-to-semantic installer projection boundary; do not promote concrete evidence artifacts into semantic truth.
|