frontend-project-context 1.8.0 → 1.9.1
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 +18 -1
- package/README.md +7 -7
- package/UPGRADING.md +10 -0
- package/docs/08-INSTALLATION-AND-DISTRIBUTION.md +9 -3
- package/docs/14-FORMAL-RELEASE-READINESS.md +14 -0
- package/docs/28-REAL-PROJECT-ONBOARDING-CLOSURE-DESIGN.md +10 -0
- package/docs/29-REAL-PROJECT-1.8.0-INITIALIZATION-OBSERVATIONS.md +228 -0
- package/docs/30-TASK-CONTEXT-CONSUMPTION-CLOSURE-DESIGN.md +563 -0
- package/docs/31-TASK-CONTEXT-INTEGRITY-REPAIR-DESIGN.md +281 -0
- package/docs/AI-PROJECT-INITIALIZATION.md +3 -2
- package/docs/PRODUCT-SHARING-AND-ADOPTION-GUIDE.md +111 -0
- package/docs/README.md +14 -2
- package/docs/USER-AND-AI-OPERATION-MANUAL.md +3 -3
- package/docs/assets/product-sharing-01-overview.svg +32 -0
- package/docs/assets/product-sharing-02-how-it-works.svg +17 -0
- package/docs/assets/product-sharing-03-example.svg +19 -0
- package/examples/package.json +1 -1
- package/migration-manifest.json +30 -12
- package/package.json +2 -2
- package/schemas/capabilities.schema.json +14 -10
- package/schemas/context-bundle.schema.json +32 -0
- package/schemas/coverage-audit.schema.json +6 -4
- package/schemas/evidence-bundle.schema.json +1 -1
- package/schemas/initialization-instruction.schema.json +17 -5
- package/schemas/migration-manifest.schema.json +3 -3
- package/schemas/migration-plan.schema.json +1 -1
- package/schemas/projection-lock.schema.json +1 -1
- package/schemas/upgrade-assessment.schema.json +1 -1
- package/schemas/upgrade-result-bundle.schema.json +1 -1
- package/src/project-context/adaptive-context-schema.mjs +1 -1
- package/src/project-context/adaptive-context.mjs +12 -23
- package/src/project-context/ai-entry.mjs +22 -9
- package/src/project-context/approver.mjs +6 -2
- package/src/project-context/authoring.mjs +28 -8
- package/src/project-context/capabilities.mjs +4 -1
- package/src/project-context/checker.mjs +1 -1
- package/src/project-context/cli.mjs +29 -16
- package/src/project-context/context-bundle.mjs +378 -0
- package/src/project-context/contract-schema.mjs +62 -9
- package/src/project-context/coverage-profile.mjs +127 -0
- package/src/project-context/exchange-schema.mjs +23 -6
- package/src/project-context/exchange.mjs +3 -0
- package/src/project-context/initialization-instruction.mjs +20 -2
- package/src/project-context/maintenance.mjs +5 -4
- package/src/project-context/migration-manifest.mjs +3 -3
- package/src/project-context/renderer.mjs +11 -4
- package/src/project-context/source-reader.mjs +27 -2
- package/src/project-context/upgrade-schema.mjs +1 -1
package/migration-manifest.json
CHANGED
|
@@ -23,14 +23,15 @@
|
|
|
23
23
|
"consumerChanges": {
|
|
24
24
|
"actionPlan": {"reason": "Action Plan schema 1 and 2 readers remain compatible; writers remain schema 2.", "state": "preserve"},
|
|
25
25
|
"adaptiveContextBundle": {"reason": "Schema 1 Adaptive Context Bundles lack task lineage and strict withheld-delivery semantics; regenerate as schema 2.", "state": "regenerate"},
|
|
26
|
-
"capabilities": {"reason": "Consumers must branch on capabilities schema
|
|
26
|
+
"capabilities": {"reason": "Consumers must branch on capabilities schema 9 and exchange protocol 9.", "state": "regenerate"},
|
|
27
|
+
"contextBundle": {"reason": "Context Bundle schema 1 is short-lived and must be regenerated from the current Contract and exact task paths.", "state": "regenerate"},
|
|
27
28
|
"contextQuery": {"reason": "Context Query schema 1 is short-lived and must use current snapshots.", "state": "regenerate"},
|
|
28
|
-
"coverageAudit": {"reason": "Coverage Audit schema
|
|
29
|
+
"coverageAudit": {"reason": "Coverage Audit schema 2 is derived from the current Contract and declared coverage profile.", "state": "regenerate"},
|
|
29
30
|
"evidenceBundle": {"reason": "Evidence Bundle schema 1 remains readable and separate from upgrade results.", "state": "preserve"},
|
|
30
31
|
"evidenceInput": {"reason": "Evidence Input schema 1 remains readable and is never rewritten by upgrade.", "state": "preserve"},
|
|
31
|
-
"exchange": {"reason": "Exchange protocol capability metadata changes to version
|
|
32
|
+
"exchange": {"reason": "Exchange protocol capability metadata changes to version 9.", "state": "regenerate"},
|
|
32
33
|
"hostPromotionEvidence": {"reason": "Host Promotion Evidence is short-lived and remains bound to exact code, artifact, and Project Context snapshots.", "state": "regenerate"},
|
|
33
|
-
"initializationInstruction": {"reason": "Schema
|
|
34
|
+
"initializationInstruction": {"reason": "Schema 2 adds concentrated human entry rule dispositions and remains package-derived.", "state": "regenerate"},
|
|
34
35
|
"integrationReviewBundle": {"reason": "Short-lived integration review bundles must be regenerated against current baselines.", "state": "regenerate"},
|
|
35
36
|
"projectStatus": {"reason": "Schema 2 separates governance health from Contract readiness and task-context routing.", "state": "regenerate"},
|
|
36
37
|
"reviewBundle": {"reason": "Review Bundle schema 2 remains readable but short-lived reviews must be regenerated when their baselines change.", "state": "regenerate"},
|
|
@@ -42,8 +43,8 @@
|
|
|
42
43
|
,"truthReconciliationReviewBundle": {"reason": "Schema 1 reviews lack finding digests and machine-verifiable resolution outcomes; recompute as schema 2.", "state": "regenerate"}
|
|
43
44
|
},
|
|
44
45
|
"externalEffects": {"automaticUpgrade": false, "businessCode": false, "git": false, "network": false, "packageManager": false, "projectTests": false},
|
|
45
|
-
"manifestDigest": "sha256:
|
|
46
|
-
"package": {"name": "frontend-project-context", "version": "1.
|
|
46
|
+
"manifestDigest": "sha256:8b1a6bce6ac179591b122593c062cf6d0f80680da1eca5aba663e3273bbd1e56",
|
|
47
|
+
"package": {"name": "frontend-project-context", "version": "1.9.1"},
|
|
47
48
|
"paths": [
|
|
48
49
|
{
|
|
49
50
|
"acceptance": ["project-context-clean", "target-renderers-current"],
|
|
@@ -84,18 +85,35 @@
|
|
|
84
85
|
"migrationIds": ["upgrade.republish-ai-entry.v1"],
|
|
85
86
|
"requiresHumanReview": false,
|
|
86
87
|
"rollbackClass": "package-only"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"acceptance": ["project-context-clean", "target-renderers-current"],
|
|
91
|
+
"classification": "package-only",
|
|
92
|
+
"fromVersion": "1.8.0",
|
|
93
|
+
"migrationIds": ["upgrade.republish-ai-entry.v1"],
|
|
94
|
+
"requiresHumanReview": false,
|
|
95
|
+
"rollbackClass": "package-only"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"acceptance": ["project-context-clean", "target-renderers-current"],
|
|
99
|
+
"classification": "package-only",
|
|
100
|
+
"fromVersion": "1.9.0",
|
|
101
|
+
"migrationIds": [],
|
|
102
|
+
"requiresHumanReview": false,
|
|
103
|
+
"rollbackClass": "package-only"
|
|
87
104
|
}
|
|
88
105
|
],
|
|
89
106
|
"protocols": {
|
|
90
107
|
"actionPlan": {"readable": [1, 2], "written": 2},
|
|
91
108
|
"adaptiveContextBundle": {"readable": [2], "written": 2},
|
|
109
|
+
"contextBundle": {"readable": [1], "written": 1},
|
|
92
110
|
"contextQuery": {"readable": [2], "written": 2},
|
|
93
|
-
"coverageAudit": {"readable": [1], "written":
|
|
111
|
+
"coverageAudit": {"readable": [1, 2], "written": 2},
|
|
94
112
|
"evidenceBundle": {"readable": [1], "written": 1},
|
|
95
113
|
"evidenceInput": {"readable": [1], "written": 1},
|
|
96
|
-
"exchange": {"readable": [4, 5, 6, 7, 8], "written":
|
|
114
|
+
"exchange": {"readable": [4, 5, 6, 7, 8, 9], "written": 9},
|
|
97
115
|
"hostPromotionEvidence": {"readable": [1], "written": 1},
|
|
98
|
-
"initializationInstruction": {"readable": [1], "written":
|
|
116
|
+
"initializationInstruction": {"readable": [1, 2], "written": 2},
|
|
99
117
|
"integrationReviewBundle": {"readable": [1], "written": 1},
|
|
100
118
|
"projectStatus": {"readable": [1, 2], "written": 2},
|
|
101
119
|
"reviewBundle": {"readable": [1, 2], "written": 2},
|
|
@@ -107,16 +125,16 @@
|
|
|
107
125
|
"truthReconciliationReviewBundle": {"readable": [2], "written": 2}
|
|
108
126
|
},
|
|
109
127
|
"renderers": {
|
|
110
|
-
"aiEntry": {"readable": [1, 2, 3, 4], "written":
|
|
128
|
+
"aiEntry": {"readable": [1, 2, 3, 4, 5], "written": 5},
|
|
111
129
|
"projection": {"readable": [1, 2, 3], "written": 3}
|
|
112
130
|
},
|
|
113
131
|
"rollback": {"automatic": false, "defaultClass": "package-only", "externalRestoreRequired": true},
|
|
114
132
|
"schemaVersion": 2,
|
|
115
133
|
"stores": {
|
|
116
|
-
"contract": {"readable": [1, 2], "written": [1, 2]},
|
|
134
|
+
"contract": {"readable": [1, 2, 3], "written": [1, 2, 3]},
|
|
117
135
|
"projectionLock": {"readable": [1, 2], "written": [1, 2]},
|
|
118
136
|
"proposal": {"readable": [1], "written": [1]},
|
|
119
137
|
"sourceLock": {"readable": [1], "written": [1]}
|
|
120
138
|
},
|
|
121
|
-
"upgradeFrom": ["1.3.1", "1.4.0", "1.5.0", "1.6.0", "1.7.0"]
|
|
139
|
+
"upgradeFrom": ["1.3.1", "1.4.0", "1.5.0", "1.6.0", "1.7.0", "1.8.0", "1.9.0"]
|
|
122
140
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontend-project-context",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "Govern, compile, and verify project-local context for AI coding tools.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"check": "node --check bin/project-context.mjs && npm test",
|
|
30
|
-
"test": "node --test test/project-context/acceptance.test.mjs test/project-context/adaptive-context.test.mjs test/project-context/adaptive-delivery-v2.test.mjs test/project-context/assist.test.mjs test/project-context/cli.test.mjs test/project-context/dashboard.test.mjs test/project-context/evidence.test.mjs test/project-context/exchange.test.mjs test/project-context/maintenance.test.mjs test/project-context/onboarding-closure.test.mjs test/project-context/source-lifecycle.test.mjs test/project-context/task-context.test.mjs test/project-context/takeover.test.mjs test/project-context/truth-reconciliation.test.mjs test/project-context/upgrade.test.mjs test/release/acceptance.test.mjs",
|
|
30
|
+
"test": "node --test test/project-context/acceptance.test.mjs test/project-context/adaptive-context.test.mjs test/project-context/adaptive-delivery-v2.test.mjs test/project-context/assist.test.mjs test/project-context/cli.test.mjs test/project-context/dashboard.test.mjs test/project-context/evidence.test.mjs test/project-context/exchange.test.mjs test/project-context/maintenance.test.mjs test/project-context/onboarding-closure.test.mjs test/project-context/source-lifecycle.test.mjs test/project-context/task-context.test.mjs test/project-context/task-context-consumption.test.mjs test/project-context/takeover.test.mjs test/project-context/truth-reconciliation.test.mjs test/project-context/upgrade.test.mjs test/release/acceptance.test.mjs",
|
|
31
31
|
"prepack": "npm run check"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "urn:frontend-project-context:schema:capabilities:
|
|
3
|
+
"$id": "urn:frontend-project-context:schema:capabilities:9",
|
|
4
4
|
"title": "Frontend Project Context Capabilities",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
@@ -19,17 +19,17 @@
|
|
|
19
19
|
"boundaries"
|
|
20
20
|
],
|
|
21
21
|
"properties": {
|
|
22
|
-
"schemaVersion": { "const":
|
|
22
|
+
"schemaVersion": { "const": 9 },
|
|
23
23
|
"package": {
|
|
24
24
|
"type": "object",
|
|
25
25
|
"additionalProperties": false,
|
|
26
26
|
"required": ["name", "version"],
|
|
27
27
|
"properties": {
|
|
28
28
|
"name": { "const": "frontend-project-context" },
|
|
29
|
-
"version": { "const": "1.
|
|
29
|
+
"version": { "const": "1.9.1" }
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
|
-
"exchangeProtocolVersion": { "const":
|
|
32
|
+
"exchangeProtocolVersion": { "const": 9 },
|
|
33
33
|
"schemas": {
|
|
34
34
|
"type": "object",
|
|
35
35
|
"additionalProperties": false,
|
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
"assistBundle",
|
|
40
40
|
"capabilities",
|
|
41
41
|
"contract",
|
|
42
|
+
"contractReadable",
|
|
43
|
+
"contextBundle",
|
|
42
44
|
"contextQuery",
|
|
43
45
|
"coverageAudit",
|
|
44
46
|
"dashboardViewModel",
|
|
@@ -71,23 +73,25 @@
|
|
|
71
73
|
"actionPlan": { "const": 2 },
|
|
72
74
|
"adaptiveContextBundle": { "const": 2 },
|
|
73
75
|
"assistBundle": { "const": 1 },
|
|
74
|
-
"capabilities": { "const":
|
|
75
|
-
"contract": { "const":
|
|
76
|
+
"capabilities": { "const": 9 },
|
|
77
|
+
"contract": { "const": 3 },
|
|
78
|
+
"contractReadable": { "const": [1, 2, 3] },
|
|
79
|
+
"contextBundle": { "const": 1 },
|
|
76
80
|
"contextQuery": { "const": 2 },
|
|
77
|
-
"coverageAudit": { "const":
|
|
81
|
+
"coverageAudit": { "const": 2 },
|
|
78
82
|
"dashboardViewModel": { "type": "integer" },
|
|
79
83
|
"evidenceBundle": { "const": 1 },
|
|
80
84
|
"evidenceInput": { "const": 1 },
|
|
81
85
|
"hostPromotionEvidence": { "const": 1 },
|
|
82
86
|
"integrationReviewBundle": { "const": 1 },
|
|
83
|
-
"initializationInstruction": { "const":
|
|
87
|
+
"initializationInstruction": { "const": 2 },
|
|
84
88
|
"projectionLock": { "const": 2 },
|
|
85
89
|
"projectionLockReadable": { "const": [1, 2] },
|
|
86
90
|
"projectionLockWritten": { "enum": [1, 2] },
|
|
87
91
|
"projectionRenderer": { "type": "integer" },
|
|
88
92
|
"proposal": { "const": 1 },
|
|
89
93
|
"projectStatus": { "const": 2 },
|
|
90
|
-
"aiEntryRenderer": { "const":
|
|
94
|
+
"aiEntryRenderer": { "const": 5 },
|
|
91
95
|
"migrationManifest": { "const": 2 },
|
|
92
96
|
"migrationPlan": { "const": 1 },
|
|
93
97
|
"reviewBundle": { "const": 2 },
|
|
@@ -139,7 +143,7 @@
|
|
|
139
143
|
"required": ["id", "version", "packagePath", "digest", "command"],
|
|
140
144
|
"properties": {
|
|
141
145
|
"id": {"const": "ai-project-initialization"},
|
|
142
|
-
"version": {"const":
|
|
146
|
+
"version": {"const": 2},
|
|
143
147
|
"packagePath": {"const": "docs/AI-PROJECT-INITIALIZATION.md"},
|
|
144
148
|
"digest": {"type": "string", "pattern": "^sha256:[a-f0-9]{64}$"},
|
|
145
149
|
"command": {"const": "project-context instructions --project PATH [--json | --prompt]"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:frontend-project-context:schema:context-bundle:1",
|
|
4
|
+
"title": "Frontend Project Context Bundle",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "kind", "project", "mode", "task", "snapshots", "semanticCompleteness", "commonItemIds", "targets", "items", "readTargets", "provenanceSources", "coverage", "authority", "gaps", "nextActions", "content", "bundleDigest"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": {"const": 1},
|
|
10
|
+
"kind": {"const": "context-bundle"},
|
|
11
|
+
"project": {"type": "object", "additionalProperties": false, "required": ["id", "name"], "properties": {"id": {"type": "string", "minLength": 1}, "name": {"type": "string", "minLength": 1}}},
|
|
12
|
+
"mode": {"enum": ["locate", "targeted"]},
|
|
13
|
+
"task": {"type": "object", "additionalProperties": false, "required": ["text", "paths"], "properties": {"text": {"type": "string"}, "paths": {"$ref": "#/$defs/strings"}}},
|
|
14
|
+
"snapshots": {"type": "object", "additionalProperties": false, "required": ["contract", "sourcesLock", "projectionsLock"], "properties": {"contract": {"$ref": "#/$defs/digest"}, "sourcesLock": {"$ref": "#/$defs/digest"}, "projectionsLock": {"$ref": "#/$defs/digest"}}},
|
|
15
|
+
"semanticCompleteness": {"const": "not-claimed"},
|
|
16
|
+
"commonItemIds": {"$ref": "#/$defs/strings"},
|
|
17
|
+
"targets": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["path", "scopeCoverage", "itemIds", "scopedItemIds", "deltaItemIds"], "properties": {"path": {"type": "string"}, "scopeCoverage": {"enum": ["scoped", "project-only", "none"]}, "itemIds": {"$ref": "#/$defs/strings"}, "scopedItemIds": {"$ref": "#/$defs/strings"}, "deltaItemIds": {"$ref": "#/$defs/strings"}}}},
|
|
18
|
+
"items": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["id", "kind", "subject", "scope", "sourceIds", "statement", "value", "overrides"], "properties": {"id": {"type": "string", "minLength": 1}, "kind": {"enum": ["fact", "policy", "reference", "validation-description"]}, "subject": {"type": "string", "minLength": 1}, "scope": {"type": "object", "additionalProperties": false, "required": ["kind"], "properties": {"kind": {"enum": ["project", "path-prefix", "file"]}, "path": {"type": "string"}}}, "sourceIds": {"$ref": "#/$defs/strings"}, "statement": {"type": "string", "minLength": 1}, "value": true, "overrides": {"$ref": "#/$defs/strings"}, "verification": {"type": "object", "additionalProperties": false, "required": ["kind"], "properties": {"kind": {"enum": ["none", "file-exists", "json-value", "path-digest"]}, "source": {"type": "string"}, "expected": true}}, "consumption": {"type": "object", "additionalProperties": false, "required": ["requiredSources", "conditionalSources"], "properties": {"requiredSources": {"$ref": "#/$defs/strings"}, "conditionalSources": {"$ref": "#/$defs/strings"}}}}}},
|
|
19
|
+
"readTargets": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["path", "role", "itemIds", "sourceIds", "reasons"], "properties": {"path": {"type": "string"}, "role": {"enum": ["required", "conditional"]}, "itemIds": {"$ref": "#/$defs/strings"}, "sourceIds": {"$ref": "#/$defs/strings"}, "reasons": {"$ref": "#/$defs/strings"}, "pointers": {"$ref": "#/$defs/strings"}}}},
|
|
20
|
+
"provenanceSources": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["sourceId", "itemIds", "kind", "locator"], "properties": {"sourceId": {"type": "string"}, "itemIds": {"$ref": "#/$defs/strings"}, "kind": {"type": "string"}, "locator": {"type": "object"}}}},
|
|
21
|
+
"coverage": {"type": "object", "additionalProperties": false, "required": ["profileItemId", "profileVersion", "registrationCoverage", "targets", "guarantee"], "properties": {"profileItemId": {"type": ["string", "null"]}, "profileVersion": {"enum": [null, 1, 2]}, "registrationCoverage": {"enum": ["not-declared", "review-required", "unresolved", "closed-for-declared-scope"]}, "targets": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["path", "disposition", "domainId"], "properties": {"path": {"type": "string"}, "disposition": {"enum": ["contract-covered", "dynamic-investigation", "excluded-approved", "unresolved", "outside-declared-coverage"]}, "domainId": {"type": ["string", "null"]}}}}, "guarantee": {"const": "declared-scope-only-never-all-project-truth"}}},
|
|
22
|
+
"authority": {"type": "object", "additionalProperties": false, "required": ["productTaskExecution", "productBusinessCodeWrites", "hostTaskAuthority", "resolveFrom"], "properties": {"productTaskExecution": {"const": false}, "productBusinessCodeWrites": {"const": false}, "hostTaskAuthority": {"const": "external-to-project-context"}, "resolveFrom": {"const": ["current-user-request", "human-authored-project-instructions", "higher-priority-safety-constraints"]}}},
|
|
23
|
+
"gaps": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["code"], "properties": {"code": {"enum": ["target-path-not-yet-known", "project-scope-only", "no-applicable-contract-item", "registration-coverage-not-declared", "registration-coverage-review-required", "required-read-target-unavailable"]}, "path": {"type": "string"}, "sourceIds": {"$ref": "#/$defs/strings"}, "reason": {"type": "string"}, "disposition": {"enum": ["unresolved"]}}}},
|
|
24
|
+
"nextActions": {"$ref": "#/$defs/strings"},
|
|
25
|
+
"content": {"type": "string", "minLength": 1},
|
|
26
|
+
"bundleDigest": {"$ref": "#/$defs/digest"}
|
|
27
|
+
},
|
|
28
|
+
"$defs": {
|
|
29
|
+
"digest": {"type": "string", "pattern": "^sha256:[a-f0-9]{64}$"},
|
|
30
|
+
"strings": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "urn:frontend-project-context:schema:coverage-audit:
|
|
3
|
+
"$id": "urn:frontend-project-context:schema:coverage-audit:2",
|
|
4
4
|
"title": "Frontend Project Context Coverage Audit",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
7
|
"required": ["schemaVersion", "kind", "project", "snapshots", "profile", "registrationCoverage", "categories", "guarantee", "auditDigest"],
|
|
8
8
|
"properties": {
|
|
9
|
-
"schemaVersion": { "const":
|
|
9
|
+
"schemaVersion": { "const": 2 },
|
|
10
10
|
"kind": { "const": "coverage-audit" },
|
|
11
11
|
"project": { "type": "object", "additionalProperties": false, "required": ["id", "name"], "properties": { "id": { "type": "string" }, "name": { "type": "string" } } },
|
|
12
12
|
"snapshots": { "$ref": "context-query.schema.json#/$defs/snapshots" },
|
|
13
13
|
"profile": { "type": ["object", "null"] },
|
|
14
|
-
"registrationCoverage": { "enum": ["not-declared", "review-required", "closed-for-declared-scope"] },
|
|
14
|
+
"registrationCoverage": { "enum": ["not-declared", "review-required", "unresolved", "closed-for-declared-scope"] },
|
|
15
15
|
"categories": {
|
|
16
16
|
"type": "object",
|
|
17
17
|
"additionalProperties": false,
|
|
18
|
-
"required": ["registered", "excluded-approved", "review-required", "outside-declared-coverage"],
|
|
18
|
+
"required": ["registered", "dynamic-investigation", "excluded-approved", "review-required", "unresolved", "outside-declared-coverage"],
|
|
19
19
|
"properties": {
|
|
20
20
|
"registered": { "$ref": "#/$defs/candidates" },
|
|
21
|
+
"dynamic-investigation": { "$ref": "#/$defs/candidates" },
|
|
21
22
|
"excluded-approved": { "$ref": "#/$defs/candidates" },
|
|
22
23
|
"review-required": { "$ref": "#/$defs/candidates" },
|
|
24
|
+
"unresolved": { "$ref": "#/$defs/candidates" },
|
|
23
25
|
"outside-declared-coverage": { "$ref": "#/$defs/candidates" }
|
|
24
26
|
}
|
|
25
27
|
},
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"required": ["name", "version", "exchangeProtocolVersion"],
|
|
15
15
|
"properties": {
|
|
16
16
|
"name": { "const": "frontend-project-context" },
|
|
17
|
-
"version": { "const": "1.
|
|
17
|
+
"version": { "const": "1.9.1" },
|
|
18
18
|
"exchangeProtocolVersion": { "const": 6 }
|
|
19
19
|
}
|
|
20
20
|
},
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "urn:frontend-project-context:schema:initialization-instruction:
|
|
3
|
+
"$id": "urn:frontend-project-context:schema:initialization-instruction:2",
|
|
4
4
|
"title": "Frontend Project Context Initialization Instruction",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
|
-
"required": ["schemaVersion", "package", "instruction", "targetRoot", "boundaries"],
|
|
7
|
+
"required": ["schemaVersion", "package", "instruction", "targetRoot", "boundaries", "entryRuleReview"],
|
|
8
8
|
"properties": {
|
|
9
|
-
"schemaVersion": {"const":
|
|
9
|
+
"schemaVersion": {"const": 2},
|
|
10
10
|
"package": {
|
|
11
11
|
"type": "object",
|
|
12
12
|
"additionalProperties": false,
|
|
13
13
|
"required": ["name", "version"],
|
|
14
14
|
"properties": {
|
|
15
15
|
"name": {"const": "frontend-project-context"},
|
|
16
|
-
"version": {"const": "1.
|
|
16
|
+
"version": {"const": "1.9.1"}
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"instruction": {
|
|
@@ -22,13 +22,25 @@
|
|
|
22
22
|
"required": ["id", "version", "packagePath", "digest", "content"],
|
|
23
23
|
"properties": {
|
|
24
24
|
"id": {"const": "ai-project-initialization"},
|
|
25
|
-
"version": {"const":
|
|
25
|
+
"version": {"const": 2},
|
|
26
26
|
"packagePath": {"const": "docs/AI-PROJECT-INITIALIZATION.md"},
|
|
27
27
|
"digest": {"$ref": "#/$defs/digest"},
|
|
28
28
|
"content": {"type": "string", "minLength": 1}
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"targetRoot": {"type": "string", "minLength": 1},
|
|
32
|
+
"entryRuleReview": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"additionalProperties": false,
|
|
35
|
+
"required": ["path", "present", "allowedDispositions", "records", "automaticDeletion"],
|
|
36
|
+
"properties": {
|
|
37
|
+
"path": {"const": "AGENTS.md"},
|
|
38
|
+
"present": {"type": "boolean"},
|
|
39
|
+
"allowedDispositions": {"const": ["migrated-to-contract", "kept-in-entry-intentionally", "excluded-as-stale", "unresolved-blocker"]},
|
|
40
|
+
"records": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["id", "disposition", "rationale"], "properties": {"id": {"type": "string"}, "disposition": {"enum": ["migrated-to-contract", "kept-in-entry-intentionally", "excluded-as-stale", "unresolved-blocker"]}, "rationale": {"type": "string"}}}},
|
|
41
|
+
"automaticDeletion": {"const": false}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
32
44
|
"boundaries": {
|
|
33
45
|
"type": "object",
|
|
34
46
|
"additionalProperties": false,
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"required": ["schemaVersion", "package", "upgradeFrom", "stores", "renderers", "protocols", "paths", "builtInMigrations", "consumerChanges", "acceptanceCommands", "rollback", "externalEffects", "manifestDigest"],
|
|
8
8
|
"properties": {
|
|
9
9
|
"schemaVersion": {"const": 2},
|
|
10
|
-
"package": {"type": "object", "additionalProperties": false, "required": ["name", "version"], "properties": {"name": {"const": "frontend-project-context"}, "version": {"const": "1.
|
|
11
|
-
"upgradeFrom": {"const": ["1.3.1", "1.4.0", "1.5.0", "1.6.0", "1.7.0"]},
|
|
10
|
+
"package": {"type": "object", "additionalProperties": false, "required": ["name", "version"], "properties": {"name": {"const": "frontend-project-context"}, "version": {"const": "1.9.1"}}},
|
|
11
|
+
"upgradeFrom": {"const": ["1.3.1", "1.4.0", "1.5.0", "1.6.0", "1.7.0", "1.8.0"]},
|
|
12
12
|
"stores": {"type": "object", "additionalProperties": false, "required": ["contract", "projectionLock", "proposal", "sourceLock"], "properties": {"contract": {"$ref": "#/$defs/versionMatrix"}, "projectionLock": {"$ref": "#/$defs/versionMatrix"}, "proposal": {"$ref": "#/$defs/versionMatrix"}, "sourceLock": {"$ref": "#/$defs/versionMatrix"}}},
|
|
13
13
|
"renderers": {"type": "object", "additionalProperties": false, "required": ["aiEntry", "projection"], "properties": {"aiEntry": {"$ref": "#/$defs/versionTarget"}, "projection": {"$ref": "#/$defs/versionTarget"}}},
|
|
14
14
|
"protocols": {"type": "object", "additionalProperties": {"$ref": "#/$defs/versionTarget"}},
|
|
15
|
-
"paths": {"type": "array", "minItems":
|
|
15
|
+
"paths": {"type": "array", "minItems": 6, "maxItems": 6, "items": {"type": "object", "additionalProperties": false, "required": ["fromVersion", "classification", "migrationIds", "requiresHumanReview", "rollbackClass", "acceptance"], "properties": {"fromVersion": {"enum": ["1.3.1", "1.4.0", "1.5.0", "1.6.0", "1.7.0", "1.8.0"]}, "classification": {"enum": ["package-only", "republish-ai-entry", "republish-projection", "built-in-store-migration", "invalidate-ephemeral-protocol"]}, "migrationIds": {"$ref": "#/$defs/strings"}, "requiresHumanReview": {"type": "boolean"}, "rollbackClass": {"$ref": "#/$defs/rollbackClass"}, "acceptance": {"$ref": "#/$defs/strings"}}}},
|
|
16
16
|
"builtInMigrations": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["id", "kind", "input", "targetType", "writes", "rollbackClass", "reverseMigrationId"], "properties": {"id": {"type": "string"}, "kind": {"enum": ["package-only", "republish-ai-entry", "republish-projection", "built-in-store-migration", "invalidate-ephemeral-protocol"]}, "input": {"type": "object", "additionalProperties": false, "required": ["schemaVersions", "beforeDigestRequired"], "properties": {"schemaVersions": {"type": "array", "items": {"type": "integer", "minimum": 1}}, "beforeDigestRequired": {"type": "boolean"}}}, "targetType": {"enum": ["none", "ai-entry", "projection", "store", "ephemeral-protocol"]}, "writes": {"type": "boolean"}, "rollbackClass": {"$ref": "#/$defs/rollbackClass"}, "reverseMigrationId": {"type": ["string", "null"]}}}},
|
|
17
17
|
"consumerChanges": {"type": "object", "additionalProperties": {"type": "object", "additionalProperties": false, "required": ["state", "reason"], "properties": {"state": {"enum": ["preserve", "regenerate", "invalidate"]}, "reason": {"type": "string"}}}},
|
|
18
18
|
"acceptanceCommands": {"$ref": "#/$defs/strings"},
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"schemaVersion": {"const": 1},
|
|
10
10
|
"kind": {"const": "target-upgrade-plan"},
|
|
11
11
|
"fromVersion": {"enum": ["1.3.1", "1.4.0", "1.5.0", "1.6.0", "1.7.0"]},
|
|
12
|
-
"targetVersion": {"const": "1.
|
|
12
|
+
"targetVersion": {"const": "1.9.1"},
|
|
13
13
|
"manifestDigest": {"$ref": "#/$defs/digest"},
|
|
14
14
|
"assessmentDigest": {"$ref": "#/$defs/digest"},
|
|
15
15
|
"snapshots": {"anyOf": [{"type": "null"}, {"$ref": "#/$defs/snapshots"}]},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"properties": {
|
|
34
34
|
"path": { "type": "string" }, "target": { "const": "ai-entry" }, "ownership": { "const": "region" },
|
|
35
35
|
"regionId": { "const": "project-context-ai-entry" }, "regionDigest": { "$ref": "#/$defs/digest" },
|
|
36
|
-
"rendererVersion": { "enum": [1, 2, 3] }, "createdFile": { "type": "boolean" }
|
|
36
|
+
"rendererVersion": { "enum": [1, 2, 3, 4, 5] }, "createdFile": { "type": "boolean" }
|
|
37
37
|
}
|
|
38
38
|
},
|
|
39
39
|
"schema1": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"kind": {"const": "upgrade-assessment"},
|
|
11
11
|
"product": {"type": "object", "additionalProperties": false, "required": ["name"], "properties": {"name": {"const": "frontend-project-context"}}},
|
|
12
12
|
"fromVersion": {"enum": ["1.3.1", "1.4.0", "1.5.0", "1.6.0", "1.7.0"]},
|
|
13
|
-
"targetVersion": {"const": "1.
|
|
13
|
+
"targetVersion": {"const": "1.9.1"},
|
|
14
14
|
"fromVersionEvidence": {"const": "host-asserted"},
|
|
15
15
|
"manifestSchemaVersion": {"const": 2},
|
|
16
16
|
"manifestDigest": {"$ref": "#/$defs/digest"},
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"kind": {"const": "upgrade-result-bundle"},
|
|
11
11
|
"product": {"type": "object", "additionalProperties": false, "required": ["name"], "properties": {"name": {"const": "frontend-project-context"}}},
|
|
12
12
|
"fromVersion": {"type": "string"},
|
|
13
|
-
"targetVersion": {"const": "1.
|
|
13
|
+
"targetVersion": {"const": "1.9.1"},
|
|
14
14
|
"manifestDigest": {"$ref": "#/$defs/digest"},
|
|
15
15
|
"planDigest": {"$ref": "#/$defs/digest"},
|
|
16
16
|
"mode": {"enum": ["preview", "write"]},
|
|
@@ -5,7 +5,7 @@ import { normalizeRelativePath } from "./path-policy.mjs";
|
|
|
5
5
|
export const CONTEXT_QUERY_SCHEMA_VERSION = 2;
|
|
6
6
|
export const ADAPTIVE_CONTEXT_BUNDLE_SCHEMA_VERSION = 2;
|
|
7
7
|
export const ROUTING_INDEX_SCHEMA_VERSION = 2;
|
|
8
|
-
export const COVERAGE_AUDIT_SCHEMA_VERSION =
|
|
8
|
+
export const COVERAGE_AUDIT_SCHEMA_VERSION = 2;
|
|
9
9
|
export const ADAPTIVE_SELECTOR_VERSION = 2;
|
|
10
10
|
export const ROUTING_INDEX_PATH = ".project-context/derived/routing-index.json";
|
|
11
11
|
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { canonicalJson, digestJson, prettyCanonicalJson, sha256 } from "./canonical-json.mjs";
|
|
4
4
|
import { blockingContextFindings, checkProject } from "./checker.mjs";
|
|
5
5
|
import { sourceStatus } from "./contract-schema.mjs";
|
|
6
|
+
import { coverageClass, coverageProfile } from "./coverage-profile.mjs";
|
|
6
7
|
import { discoverProject } from "./discovery.mjs";
|
|
7
8
|
import { ProjectContextError, fail } from "./errors.mjs";
|
|
8
9
|
import { atomicWriteFile, readJsonFile } from "./io.mjs";
|
|
@@ -26,7 +27,6 @@ import {
|
|
|
26
27
|
const LOCAL_SOURCE_KINDS = new Set(["file", "path", "json-pointer"]);
|
|
27
28
|
const MANDATORY_KINDS = new Set(["policy", "validation-description"]);
|
|
28
29
|
const ROUTED_KINDS = new Set(["fact", "reference"]);
|
|
29
|
-
const COVERAGE_SUBJECT = "project.registration-coverage";
|
|
30
30
|
const RETRIEVAL_SUBJECT = "project.context-retrieval";
|
|
31
31
|
const EXCLUDED_BODIES = Object.freeze(["chat-history", "git-diffs", "source-bodies", "task-path-bodies", "verification-logs"]);
|
|
32
32
|
const ROUTING_STOP_TERMS = new Set(["a", "an", "and", "apply", "change", "for", "in", "of", "on", "project", "rule", "the", "to", "update", "use"]);
|
|
@@ -145,24 +145,7 @@ export async function indexContext(root, project, options = {}) {
|
|
|
145
145
|
return index;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
function coverageProfile(contract) {
|
|
150
|
-
const candidates = contract.items.filter((item) => item.status === "approved" && item.kind === "policy" && item.subject === COVERAGE_SUBJECT);
|
|
151
|
-
if (candidates.length === 0) return null;
|
|
152
|
-
if (candidates.length > 1) fail("coverage-profile-conflict", `multiple approved ${COVERAGE_SUBJECT} items are active`);
|
|
153
|
-
const item = candidates[0];
|
|
154
|
-
const value = item.value;
|
|
155
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((key) => !["excludedPaths", "roots"].includes(key)) || !Array.isArray(value.roots) || !Array.isArray(value.excludedPaths)) fail("coverage-profile-invalid", `${item.id} coverage value is invalid`);
|
|
156
|
-
const normalize = (values, label) => uniqueSorted(values.map((entry) => normalizeRelativePath(entry, { allowRoot: true, label })));
|
|
157
|
-
return { itemId: item.id, itemDigest: digestJson(item), sourceIds: [...item.sources].sort(), roots: normalize(value.roots, "coverage root"), excludedPaths: normalize(value.excludedPaths, "coverage excluded path") };
|
|
158
|
-
}
|
|
159
|
-
function coverageClass(profile, registeredPaths, candidatePath) {
|
|
160
|
-
if (registeredPaths.has(candidatePath)) return "registered";
|
|
161
|
-
if (!profile) return "outside-declared-coverage";
|
|
162
|
-
if (profile.excludedPaths.some((entry) => pathWithin(candidatePath, entry))) return "excluded-approved";
|
|
163
|
-
if (profile.roots.some((entry) => pathWithin(candidatePath, entry))) return "review-required";
|
|
164
|
-
return "outside-declared-coverage";
|
|
165
|
-
}
|
|
148
|
+
export { coverageClass, coverageProfile } from "./coverage-profile.mjs";
|
|
166
149
|
export async function buildCoverageAudit(root, project, changedPaths = []) {
|
|
167
150
|
const profile = coverageProfile(project.contract);
|
|
168
151
|
const proposal = await discoverProject(root, project.contract);
|
|
@@ -175,10 +158,16 @@ export async function buildCoverageAudit(root, project, changedPaths = []) {
|
|
|
175
158
|
if (!byPath.has(normalized)) byPath.set(normalized, { path: normalized, origin: "host-changed-path-signal" });
|
|
176
159
|
}
|
|
177
160
|
for (const registeredPath of registered) if (!byPath.has(registeredPath)) byPath.set(registeredPath, { path: registeredPath, origin: "registered-source" });
|
|
178
|
-
const categories = { registered: [], "excluded-approved": [], "review-required": [], "outside-declared-coverage": [] };
|
|
161
|
+
const categories = { registered: [], "dynamic-investigation": [], "excluded-approved": [], "review-required": [], unresolved: [], "outside-declared-coverage": [] };
|
|
179
162
|
for (const candidate of [...byPath.values()].sort((left, right) => left.path.localeCompare(right.path))) categories[coverageClass(profile, registeredPaths, candidate.path)].push(candidate);
|
|
180
|
-
const registrationCoverage = !profile
|
|
181
|
-
|
|
163
|
+
const registrationCoverage = !profile
|
|
164
|
+
? "not-declared"
|
|
165
|
+
: categories.unresolved.length > 0
|
|
166
|
+
? "unresolved"
|
|
167
|
+
: categories["review-required"].length === 0
|
|
168
|
+
? "closed-for-declared-scope"
|
|
169
|
+
: "review-required";
|
|
170
|
+
return sealArtifact({ schemaVersion: 2, kind: "coverage-audit", project: { id: project.contract.project.id, name: project.contract.project.name }, snapshots: snapshots(project), profile, registrationCoverage, categories, guarantee: "declared-scope-only-never-all-project-truth" }, "auditDigest");
|
|
182
171
|
}
|
|
183
172
|
|
|
184
173
|
function effectiveByTarget(contract, paths, findings) {
|
|
@@ -331,7 +320,7 @@ async function auditRelevantSources(root, project, items, sourceReadContext) {
|
|
|
331
320
|
const base = { id: source.id, kind: source.kind, locator: sourceLocator(source), sourceDigest: digestJson(source) };
|
|
332
321
|
if (!LOCAL_SOURCE_KINDS.has(source.kind)) { evidence.push({ ...base, freshness: "unverifiable-non-local" }); continue; }
|
|
333
322
|
try {
|
|
334
|
-
const actual = await readSourceDigest(root, source, sourceReadContext);
|
|
323
|
+
const actual = await readSourceDigest(root, source, sourceReadContext, { projectionsLock: project.projectionsLock });
|
|
335
324
|
const expected = lock.get(source.id) ?? null;
|
|
336
325
|
const freshness = expected !== null && expected === source.digest && actual === expected ? "current" : "drifted";
|
|
337
326
|
evidence.push({ ...base, freshness, expectedDigest: expected, actualDigest: actual });
|
|
@@ -6,14 +6,14 @@ import { fail } from "./errors.mjs";
|
|
|
6
6
|
import { atomicWriteFile, atomicWriteJson, readJsonFile } from "./io.mjs";
|
|
7
7
|
import { normalizeRelativePath, resolveWritableInside } from "./path-policy.mjs";
|
|
8
8
|
|
|
9
|
-
export const AI_ENTRY_RENDERER_VERSION =
|
|
9
|
+
export const AI_ENTRY_RENDERER_VERSION = 5;
|
|
10
10
|
export const AI_ENTRY_REGION_ID = "project-context-ai-entry";
|
|
11
11
|
export const AI_ENTRY_START = "<!-- project-context:ai-entry:start -->";
|
|
12
12
|
export const AI_ENTRY_END = "<!-- project-context:ai-entry:end -->";
|
|
13
13
|
|
|
14
14
|
const ENTRY_LINES = [
|
|
15
15
|
AI_ENTRY_START,
|
|
16
|
-
"<!-- project-context:ai-entry; schema-version: 1; renderer-version:
|
|
16
|
+
"<!-- project-context:ai-entry; schema-version: 1; renderer-version: 5 -->",
|
|
17
17
|
"## Project Context 启动流程",
|
|
18
18
|
"",
|
|
19
19
|
"开始项目工作前,使用项目本地安装的 `frontend-project-context` CLI,不要临时下载其他版本。",
|
|
@@ -21,10 +21,11 @@ const ENTRY_LINES = [
|
|
|
21
21
|
"1. 运行 `npm exec --offline -- project-context status --project . --json`;如果本地依赖不存在,停止并报告,不要临时下载同名包。",
|
|
22
22
|
"2. 如果状态为 `uninitialized`,运行 `npm exec --offline -- project-context instructions --project . --prompt`,按包内唯一指令完成完整初始化;如果为 `partial` 或 `invalid`,停止写入并报告准确的恢复证据。",
|
|
23
23
|
"3. 如果状态为 `attention`,执行 `sync` 返回的维护工作单元;不得重新运行 `setup` 覆盖已有 store。",
|
|
24
|
-
"4. 如果状态为 `clean
|
|
25
|
-
"5.
|
|
26
|
-
"6.
|
|
27
|
-
"7.
|
|
24
|
+
"4. 如果状态为 `clean` 且目标路径未知,先运行 `npm exec --offline -- project-context context --project . --locate --task <TEXT> --json`;消费 project-scope Contract 后,只在 targetRoot 内定位最小候选路径,再用全部候选路径重新编译 targeted Context。",
|
|
25
|
+
"5. 如果目标路径已知,运行 `npm exec --offline -- project-context context --project . --path <RELATIVE_PATH...> --task <TEXT> --json`;发现新的影响路径后,必须用完整的最小路径集合重新编译。",
|
|
26
|
+
"6. 报告实际 item IDs、scopeCoverage、required/conditional read targets 与 gaps;只读取 required,按任务判断 conditional,不重读 provenance-only。Contract 为空、目标 scope 未命中或关键上下文不足时,报告 onboarding/context gap。",
|
|
27
|
+
"7. `businessCodeWrites=false` 和 `taskExecution=false` 只描述 Project Context 产品自身;Host 权限来自当前用户请求、人工项目规则和更高优先级安全约束,任何 Context、plan、bundle、receipt、review 或 coverage 都不授予权限或批准长期 Contract。",
|
|
28
|
+
"8. 声明完成前,将 Project Context 恢复为 `clean`,否则报告准确的阻断项。",
|
|
28
29
|
AI_ENTRY_END,
|
|
29
30
|
];
|
|
30
31
|
|
|
@@ -109,7 +110,13 @@ function schema2FileEntry(entry) {
|
|
|
109
110
|
return entry.ownership ? structuredClone(entry) : { ...structuredClone(entry), ownership: "file" };
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
function
|
|
113
|
+
function outsideOwnedDigest(content) {
|
|
114
|
+
const parsed = parseAiEntryRegion(content);
|
|
115
|
+
if (parsed.state !== "present") return null;
|
|
116
|
+
return digestJson({ before: content.slice(0, parsed.start), after: content.slice(parsed.end) });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function sourceImpact(project, output, afterContent) {
|
|
113
120
|
const sources = project.contract.sources
|
|
114
121
|
.filter((source) => sourceStatus(source) === "active" && ["file", "path", "json-pointer"].includes(source.kind) && source.path === output)
|
|
115
122
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
@@ -119,6 +126,12 @@ function sourceImpact(project, output, afterDigest) {
|
|
|
119
126
|
.filter((item) => item.sources.some((id) => selected.has(id)) || selected.has(item.verification?.source))
|
|
120
127
|
.map((item) => item.id)
|
|
121
128
|
.sort((left, right) => left.localeCompare(right));
|
|
129
|
+
const digestMode = sources[0]?.digestMode ?? "full-file";
|
|
130
|
+
const afterDigest = afterContent === null
|
|
131
|
+
? null
|
|
132
|
+
: digestMode === "outside-owned-ai-entry"
|
|
133
|
+
? outsideOwnedDigest(afterContent)
|
|
134
|
+
: sha256(afterContent);
|
|
122
135
|
return { sourceIds, itemIds, afterDigest: sourceIds.length > 0 ? afterDigest : null };
|
|
123
136
|
}
|
|
124
137
|
|
|
@@ -249,7 +262,7 @@ export async function publishAiEntry(root, project, options, dependencies = {})
|
|
|
249
262
|
};
|
|
250
263
|
const nextLock = nextProjectionLock(project, resolved.normalized, nextEntry);
|
|
251
264
|
const action = currentEntry && existing === nextContent && canonicalJson(currentEntry) === canonicalJson(nextEntry) ? "unchanged" : currentEntry ? "update" : "create";
|
|
252
|
-
const impact = sourceImpact(project, resolved.normalized,
|
|
265
|
+
const impact = sourceImpact(project, resolved.normalized, nextContent);
|
|
253
266
|
const result = {
|
|
254
267
|
action,
|
|
255
268
|
current: {
|
|
@@ -304,7 +317,7 @@ export async function removeAiEntry(root, project, options, dependencies = {}) {
|
|
|
304
317
|
}
|
|
305
318
|
const nextContent = `${existing.slice(0, parsed.start)}${existing.slice(parsed.end)}`;
|
|
306
319
|
const nextLock = nextProjectionLock(project, resolved.normalized, null);
|
|
307
|
-
const impact = sourceImpact(project, resolved.normalized,
|
|
320
|
+
const impact = sourceImpact(project, resolved.normalized, nextContent);
|
|
308
321
|
const result = {
|
|
309
322
|
action: "remove",
|
|
310
323
|
current: { entry: structuredClone(currentEntry), contentDigest: sha256(existing), region: parsed.region },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { canonicalJson } from "./canonical-json.mjs";
|
|
2
2
|
import {
|
|
3
|
+
promoteContractToSchema3,
|
|
3
4
|
sourceForContract,
|
|
4
5
|
sourceRegistrationShape,
|
|
5
6
|
sourceStatus,
|
|
@@ -52,7 +53,7 @@ export async function approveProposal(root, project, proposalInput, options) {
|
|
|
52
53
|
for (const sourceId of selectedSourceIds) {
|
|
53
54
|
const source = proposalSources.get(sourceId);
|
|
54
55
|
if (!source) fail("proposal-source-missing", `proposal source is missing: ${sourceId}`);
|
|
55
|
-
const actual = await readSourceDigest(root, source);
|
|
56
|
+
const actual = await readSourceDigest(root, source, undefined, { projectionsLock: project.projectionsLock });
|
|
56
57
|
if (actual !== null && actual !== source.digest) {
|
|
57
58
|
fail("proposal-source-changed", `source changed after discovery: ${sourceId}`, {
|
|
58
59
|
exitCode: 1,
|
|
@@ -61,6 +62,9 @@ export async function approveProposal(root, project, proposalInput, options) {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
const nextContract = structuredClone(project.contract);
|
|
65
|
+
if (selected.some((item) => item.consumption !== undefined) || [...selectedSourceIds].some((id) => proposalSources.get(id)?.digestMode !== undefined)) {
|
|
66
|
+
promoteContractToSchema3(nextContract);
|
|
67
|
+
}
|
|
64
68
|
for (const sourceId of selectedSourceIds) {
|
|
65
69
|
const source = proposalSources.get(sourceId);
|
|
66
70
|
const existing = nextContract.sources.find((entry) => entry.id === sourceId);
|
|
@@ -141,7 +145,7 @@ export async function approvePendingItems(root, project, options) {
|
|
|
141
145
|
}
|
|
142
146
|
let actual;
|
|
143
147
|
try {
|
|
144
|
-
actual = await readSourceDigest(root, source);
|
|
148
|
+
actual = await readSourceDigest(root, source, undefined, { projectionsLock: project.projectionsLock });
|
|
145
149
|
} catch (error) {
|
|
146
150
|
fail("pending-source-changed", `pending item source is unavailable: ${sourceId}`, {
|
|
147
151
|
exitCode: 1,
|