@takagaki/cortex-decisions-viewer 0.12.88 → 0.12.90
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/dist/confirmed.js +87 -0
- package/dist/permissions-data.js +324 -0
- package/dist/rederivation.js +12 -9
- package/dist/render.js +243 -15
- package/dist/supersedes.js +12 -5
- package/package.json +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 「人が確認したか/機械が触ってよいか」を 1 か所で答える口(cortex-engine #699 F2b・案 1)。
|
|
4
|
+
*
|
|
5
|
+
* ## 🔴 これは写しで、正本は engine にある
|
|
6
|
+
*
|
|
7
|
+
* 正本: cortex-engine `scripts/gold/confirmed.mjs`(`machineOwned`・`humanOwned`・`CONFIRMED_STATUSES`)。
|
|
8
|
+
* もう 1 つの写し: このリポの `infra/lambda/intake/confirmed.mjs`(intake はビューアを import できない)。
|
|
9
|
+
* **題材で一致を確かめる**: engine の `test/fixtures/confirmation-status-contract.json` をファイルごと
|
|
10
|
+
* `viewer/test/fixtures/` と `infra/test/fixtures/` に写し、`test/confirmation-status-parity.test.mjs`
|
|
11
|
+
* (intake は `infra/test/intake-delete-record-logic.test.mjs`)が全ケースを流す。
|
|
12
|
+
* 規則を変えるときは engine の正本・2 つの写し・題材を同じ日に直す(**tools 側で規則を足さない**)。
|
|
13
|
+
*
|
|
14
|
+
* ## 規則(記録の `status` の生の値だけを見る。確認の帳簿は見ない)
|
|
15
|
+
*
|
|
16
|
+
* - `machineOwned(path, status)` … 機械の下書き。`status === "draft"`(**完全一致・trim も小文字化もしない**)
|
|
17
|
+
* - `humanOwned(path, status)` … 人が確認した。status がその区画の語彙から draft を除いた値
|
|
18
|
+
* 決定・ルール → `active` / 用語 → `active`・`superseded` / 関係者 → `active`・`inactive`
|
|
19
|
+
* 区画が分からない path → `active`・`superseded`・`inactive`
|
|
20
|
+
* - どちらでもない値(空・前後に空白・語彙の外)は人のものでも機械のものでもない
|
|
21
|
+
* - status に文字列・null・undefined 以外(真偽値など)が来たら投げる(今の式を渡す古い呼び方を黙って通さない)
|
|
22
|
+
*
|
|
23
|
+
* 区画は path の `Cortex/<区画>/`(先頭か `/` の直後)で決める。ビューアで path が手元に無い記録は、
|
|
24
|
+
* 呼び出し側が `Cortex/<区画>/` を渡して区画だけを伝える(engine の supersedes-index の置き場と同じ意味)。
|
|
25
|
+
*
|
|
26
|
+
* ## 🔴 クライアント(`render.ts` の `CLIENT_JS`)へ `.toString()` で埋め込む
|
|
27
|
+
*
|
|
28
|
+
* **下の関数は全部クロージャを持たない function 宣言に保つこと。** 参照してよいのはこのファイルの
|
|
29
|
+
* 別の関数(同じく流し込む)だけで、定数は関数の中に書く。`export const` を関数から参照すると
|
|
30
|
+
* CommonJS で `exports.X` に書き換わり、流し込んだ先で落ちる(`LIVE_CONFIRM_LIMITS` の項に経緯)。
|
|
31
|
+
* 外へ見せる表(`CONFIRMED_STATUSES`)は関数から作る入れ物で、関数の側はそれを読まない。
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.CONFIRMED_STATUSES_ANY = exports.CONFIRMED_STATUSES = void 0;
|
|
35
|
+
exports.sectionOfPath = sectionOfPath;
|
|
36
|
+
exports.confirmedStatusesOf = confirmedStatusesOf;
|
|
37
|
+
exports.confirmedStatusChecked = confirmedStatusChecked;
|
|
38
|
+
exports.machineOwned = machineOwned;
|
|
39
|
+
exports.humanOwned = humanOwned;
|
|
40
|
+
/** path から区画を引く(`Cortex/<区画>/` を含むか。先頭か `/` の直後。分からなければ `null`) */
|
|
41
|
+
function sectionOfPath(p) {
|
|
42
|
+
const s = typeof p === "string" ? p.replace(/\\/g, "/") : "";
|
|
43
|
+
// 🔴 順番は engine の `Object.keys(CONFIRMED_STATUSES)` と同じ(決定・ルール・用語・関係者)
|
|
44
|
+
const sections = ["Decisions", "Rules", "Glossary", "Members"];
|
|
45
|
+
for (let i = 0; i < sections.length; i++) {
|
|
46
|
+
const section = sections[i];
|
|
47
|
+
if (s.indexOf("Cortex/" + section + "/") === 0 || s.indexOf("/Cortex/" + section + "/") !== -1)
|
|
48
|
+
return section;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/** 区画の「人が確認した」status(その区画の語彙から draft を除いた値)。区画が分からなければ 4 区画の和 */
|
|
53
|
+
function confirmedStatusesOf(section) {
|
|
54
|
+
if (section === "Decisions" || section === "Rules")
|
|
55
|
+
return ["active"];
|
|
56
|
+
if (section === "Glossary")
|
|
57
|
+
return ["active", "superseded"];
|
|
58
|
+
if (section === "Members")
|
|
59
|
+
return ["active", "inactive"];
|
|
60
|
+
return ["active", "superseded", "inactive"];
|
|
61
|
+
}
|
|
62
|
+
/** status の型を確かめる(文字列・null・undefined のほかは投げる) */
|
|
63
|
+
function confirmedStatusChecked(status) {
|
|
64
|
+
if (status === null || status === undefined || typeof status === "string")
|
|
65
|
+
return status;
|
|
66
|
+
throw new TypeError("confirmed: status は文字列で渡してください(実際: " + typeof status + ")");
|
|
67
|
+
}
|
|
68
|
+
/** 機械が触ってよい(下書き)か。`status === "draft"`(完全一致・trim しない) */
|
|
69
|
+
function machineOwned(_path, status) {
|
|
70
|
+
return confirmedStatusChecked(status) === "draft";
|
|
71
|
+
}
|
|
72
|
+
/** 人が確認したか。status がその区画の語彙から draft を除いた値か(完全一致・trim しない) */
|
|
73
|
+
function humanOwned(p, status) {
|
|
74
|
+
const s = confirmedStatusChecked(status);
|
|
75
|
+
if (typeof s !== "string")
|
|
76
|
+
return false;
|
|
77
|
+
return confirmedStatusesOf(sectionOfPath(p)).indexOf(s) !== -1;
|
|
78
|
+
}
|
|
79
|
+
/** 区画ごとの「人が確認した」status(engine の `CONFIRMED_STATUSES` と同じ形・テストと読み手のための入れ物) */
|
|
80
|
+
exports.CONFIRMED_STATUSES = Object.freeze({
|
|
81
|
+
Decisions: Object.freeze(confirmedStatusesOf("Decisions")),
|
|
82
|
+
Rules: Object.freeze(confirmedStatusesOf("Rules")),
|
|
83
|
+
Glossary: Object.freeze(confirmedStatusesOf("Glossary")),
|
|
84
|
+
Members: Object.freeze(confirmedStatusesOf("Members")),
|
|
85
|
+
});
|
|
86
|
+
/** 区画が分からない path の「人が確認した」status(4 区画の和) */
|
|
87
|
+
exports.CONFIRMED_STATUSES_ANY = Object.freeze(confirmedStatusesOf(null));
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PERMISSIONS_DATA = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* 🔴 生成物。手で編集しない(#841)。
|
|
6
|
+
* 正本は `infra/lambda/shared/permissions.mjs`。作り直しは `cd infra && node scripts/gen-permissions.mjs`。
|
|
7
|
+
* 操作タブの[権限]がこの値を CLIENT_JS に注入して読む(公開ページと同じ中身)。
|
|
8
|
+
*/
|
|
9
|
+
exports.PERMISSIONS_DATA = {
|
|
10
|
+
"revisedOn": "2026年9月25日",
|
|
11
|
+
"enactedOn": "2026年9月25日",
|
|
12
|
+
"tools": [
|
|
13
|
+
{
|
|
14
|
+
"id": "slack",
|
|
15
|
+
"tool": "Slack",
|
|
16
|
+
"how": "お客様の Slack ワークスペースに Cortex のアプリを追加し、ワークスペースの管理者が許可します(OAuth)。",
|
|
17
|
+
"reach": "Cortex のアプリを招待したチャンネルのうち、登録したチャンネルだけを読みます(公開・非公開とも、招待が要ります)。",
|
|
18
|
+
"tenancyText": "ワークスペースごとに接続します。",
|
|
19
|
+
"customer": {
|
|
20
|
+
"approverLabel": "許可する方",
|
|
21
|
+
"approverText": "ワークスペースの管理者",
|
|
22
|
+
"grants": [
|
|
23
|
+
{
|
|
24
|
+
"spell": "channels:read",
|
|
25
|
+
"access": "read",
|
|
26
|
+
"accessText": "読む",
|
|
27
|
+
"purpose": "チャンネルの名前と情報を確かめる"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"spell": "channels:history",
|
|
31
|
+
"access": "read",
|
|
32
|
+
"accessText": "読む",
|
|
33
|
+
"purpose": "登録した公開チャンネルの会話を読む"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"spell": "groups:history",
|
|
37
|
+
"access": "read",
|
|
38
|
+
"accessText": "読む",
|
|
39
|
+
"purpose": "登録した非公開チャンネルの会話を読む"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"spell": "users:read",
|
|
43
|
+
"access": "read",
|
|
44
|
+
"accessText": "読む",
|
|
45
|
+
"purpose": "発言した方の表示名を確かめる"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"spell": "users:read.email",
|
|
49
|
+
"access": "read",
|
|
50
|
+
"accessText": "読む",
|
|
51
|
+
"purpose": "発言した方のメールアドレスを確かめ、同じ方を見分ける"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"share": null,
|
|
55
|
+
"keyOnly": null,
|
|
56
|
+
"notRequested": [
|
|
57
|
+
{
|
|
58
|
+
"spell": "chat:write",
|
|
59
|
+
"label": "メッセージの投稿"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"cortexAccount": null,
|
|
64
|
+
"delegated": {
|
|
65
|
+
"text": "利用者ご本人が、AI の道具(Claude Code)から Slack の公式の接続口に、ご自身のアカウントでつなぎます。権限は Slack の同意画面で、ご本人が確かめて許可します。",
|
|
66
|
+
"grants": null
|
|
67
|
+
},
|
|
68
|
+
"note": null
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "github",
|
|
72
|
+
"tool": "GitHub",
|
|
73
|
+
"how": "お客様の GitHub の組織に Cortex の GitHub App をインストールし、読ませるリポジトリを選びます。",
|
|
74
|
+
"reach": "インストールで選んだリポジトリのうち、登録したリポジトリの Issue・プルリクエスト・Discussions・コミットの記録を読みます。",
|
|
75
|
+
"tenancyText": "組織ごとに接続します。",
|
|
76
|
+
"customer": {
|
|
77
|
+
"approverLabel": "許可する方",
|
|
78
|
+
"approverText": "組織のオーナー",
|
|
79
|
+
"grants": [
|
|
80
|
+
{
|
|
81
|
+
"spell": "contents",
|
|
82
|
+
"access": "read",
|
|
83
|
+
"accessText": "読む",
|
|
84
|
+
"purpose": "コミットの記録とリポジトリの中身を読む"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"spell": "issues",
|
|
88
|
+
"access": "read",
|
|
89
|
+
"accessText": "読む",
|
|
90
|
+
"purpose": "Issue とそのコメントを読む"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"spell": "pull_requests",
|
|
94
|
+
"access": "read",
|
|
95
|
+
"accessText": "読む",
|
|
96
|
+
"purpose": "プルリクエストとその会話を読む"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"spell": "discussions",
|
|
100
|
+
"access": "read",
|
|
101
|
+
"accessText": "読む",
|
|
102
|
+
"purpose": "Discussions を読む"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"spell": "metadata",
|
|
106
|
+
"access": "read",
|
|
107
|
+
"accessText": "読む",
|
|
108
|
+
"purpose": "リポジトリの基本情報を読む(GitHub が必ず付ける権限)"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"share": null,
|
|
112
|
+
"keyOnly": null,
|
|
113
|
+
"notRequested": []
|
|
114
|
+
},
|
|
115
|
+
"cortexAccount": null,
|
|
116
|
+
"delegated": null,
|
|
117
|
+
"note": null
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": "figma",
|
|
121
|
+
"tool": "Figma",
|
|
122
|
+
"how": "利用者が Figma で個人アクセストークンを発行して Cortex の画面に貼り、読ませたいファイルに発行したアカウントを閲覧者として招待します。",
|
|
123
|
+
"reach": "登録したデザインファイルだけを読みます。",
|
|
124
|
+
"tenancyText": "プロジェクトごとに接続します。",
|
|
125
|
+
"customer": {
|
|
126
|
+
"approverLabel": "許可する方",
|
|
127
|
+
"approverText": "キーやトークンを発行する方ご本人",
|
|
128
|
+
"grants": [
|
|
129
|
+
{
|
|
130
|
+
"spell": "file_content:read",
|
|
131
|
+
"access": "read",
|
|
132
|
+
"accessText": "読む",
|
|
133
|
+
"purpose": "デザインファイルの画面の一覧と説明を読む"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"spell": "file_comments:read",
|
|
137
|
+
"access": "read",
|
|
138
|
+
"accessText": "読む",
|
|
139
|
+
"purpose": "デザインファイルのコメントを読む"
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
"share": null,
|
|
143
|
+
"keyOnly": null,
|
|
144
|
+
"notRequested": []
|
|
145
|
+
},
|
|
146
|
+
"cortexAccount": null,
|
|
147
|
+
"delegated": {
|
|
148
|
+
"text": "利用者ご本人が、AI の道具(Claude Code)から Figma の公式の接続口に、ご自身のアカウントでつなぎます。権限は Figma の同意画面で、ご本人が確かめて許可します。",
|
|
149
|
+
"grants": null
|
|
150
|
+
},
|
|
151
|
+
"note": null
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"id": "drive",
|
|
155
|
+
"tool": "Google Drive",
|
|
156
|
+
"how": "読ませたいフォルダを、Cortex のサービス用アカウントに閲覧者として共有します。",
|
|
157
|
+
"reach": "共有されたフォルダのうち、登録したフォルダの資料だけを読みます。",
|
|
158
|
+
"tenancyText": "プロジェクトごとに接続します。",
|
|
159
|
+
"customer": {
|
|
160
|
+
"approverLabel": "お願いする方",
|
|
161
|
+
"approverText": "フォルダを共有する方",
|
|
162
|
+
"grants": [],
|
|
163
|
+
"share": "フォルダを閲覧者として共有する(編集の権限は要りません)",
|
|
164
|
+
"keyOnly": null,
|
|
165
|
+
"notRequested": []
|
|
166
|
+
},
|
|
167
|
+
"cortexAccount": {
|
|
168
|
+
"text": "Cortex のサービス用アカウントが、自分に共有された資料を読むために持つ権限です。",
|
|
169
|
+
"grants": [
|
|
170
|
+
{
|
|
171
|
+
"spell": "https://www.googleapis.com/auth/drive.readonly",
|
|
172
|
+
"access": "read",
|
|
173
|
+
"accessText": "読む",
|
|
174
|
+
"purpose": "共有されたフォルダの資料を読む"
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
"delegated": null,
|
|
179
|
+
"note": "このアカウントは Google Meet の議事録にも使います(その権限は Google Meet の節にあります)。"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "backlog",
|
|
183
|
+
"tool": "Backlog",
|
|
184
|
+
"how": "利用者が Backlog で API キーを発行し、Cortex の画面に貼ります。",
|
|
185
|
+
"reach": "登録したプロジェクトの課題・Wiki・ドキュメントを読みます。",
|
|
186
|
+
"tenancyText": "スペースごとに接続します。",
|
|
187
|
+
"customer": {
|
|
188
|
+
"approverLabel": "お願いする方",
|
|
189
|
+
"approverText": "キーやトークンを発行する方ご本人",
|
|
190
|
+
"grants": [],
|
|
191
|
+
"share": null,
|
|
192
|
+
"keyOnly": {
|
|
193
|
+
"access": "read",
|
|
194
|
+
"accessText": "読む",
|
|
195
|
+
"text": "Backlog の API キーには権限の範囲を絞る仕組みが無く、発行した方の権限で動きます。Cortex は読む操作だけを行います。閲覧だけができる方のキーをお勧めします。"
|
|
196
|
+
},
|
|
197
|
+
"notRequested": []
|
|
198
|
+
},
|
|
199
|
+
"cortexAccount": null,
|
|
200
|
+
"delegated": {
|
|
201
|
+
"text": "利用者ご本人が、AI の道具(Claude Code)から Cortex の Backlog の接続口に、ご自身の Backlog アカウントでつなぎます(OAuth)。ご本人の Backlog での権限の範囲で、次の操作ができます。",
|
|
202
|
+
"grants": [
|
|
203
|
+
{
|
|
204
|
+
"spell": "space",
|
|
205
|
+
"access": "read",
|
|
206
|
+
"accessText": "読む",
|
|
207
|
+
"purpose": "スペースと利用者の情報を読む"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"spell": "project",
|
|
211
|
+
"access": "write",
|
|
212
|
+
"accessText": "書く",
|
|
213
|
+
"purpose": "プロジェクトの情報を読み、追加・更新する"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"spell": "issue",
|
|
217
|
+
"access": "write",
|
|
218
|
+
"accessText": "書く",
|
|
219
|
+
"purpose": "課題とコメントを読み、追加・更新・削除する"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"spell": "wiki",
|
|
223
|
+
"access": "write",
|
|
224
|
+
"accessText": "書く",
|
|
225
|
+
"purpose": "Wiki を読み、追加・更新する"
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"spell": "document",
|
|
229
|
+
"access": "write",
|
|
230
|
+
"accessText": "書く",
|
|
231
|
+
"purpose": "ドキュメントを読み、追加する"
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
"note": null
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"id": "google-chat",
|
|
239
|
+
"tool": "Google Chat",
|
|
240
|
+
"how": "お客様の Google Workspace の管理者が Google Chat アプリ「Cortex」を許可し、読ませたいスペースにアプリを追加します。",
|
|
241
|
+
"reach": "アプリを追加したスペースのうち、登録したスペースだけを読みます。",
|
|
242
|
+
"tenancyText": "ワークスペースごとに接続します。",
|
|
243
|
+
"customer": {
|
|
244
|
+
"approverLabel": "許可する方",
|
|
245
|
+
"approverText": "ワークスペースの管理者",
|
|
246
|
+
"grants": [
|
|
247
|
+
{
|
|
248
|
+
"spell": "https://www.googleapis.com/auth/chat.app.messages.readonly",
|
|
249
|
+
"access": "read",
|
|
250
|
+
"accessText": "読む",
|
|
251
|
+
"purpose": "アプリを追加したスペースの会話を読む"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"spell": "https://www.googleapis.com/auth/chat.bot",
|
|
255
|
+
"access": "write",
|
|
256
|
+
"accessText": "書く",
|
|
257
|
+
"purpose": "スペースの名前を確かめ、通知先に設定したスペースへ確認・通知を投稿する"
|
|
258
|
+
}
|
|
259
|
+
],
|
|
260
|
+
"share": null,
|
|
261
|
+
"keyOnly": null,
|
|
262
|
+
"notRequested": []
|
|
263
|
+
},
|
|
264
|
+
"cortexAccount": null,
|
|
265
|
+
"delegated": {
|
|
266
|
+
"text": "利用者ご本人が、AI の道具(Claude Code)から Google Chat の公式の接続口に、ご自身のアカウントでつなぎます。権限は Google の同意画面で、ご本人が確かめて許可します。",
|
|
267
|
+
"grants": null
|
|
268
|
+
},
|
|
269
|
+
"note": null
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"id": "meet",
|
|
273
|
+
"tool": "Google Meet",
|
|
274
|
+
"how": "Cortex がプロジェクトごとに作るカレンダーに会議の予定を入れ、Gemini のメモを ON にします。",
|
|
275
|
+
"reach": "プロジェクトカレンダーの予定に添付されたメモと、会議名にプロジェクトの目印か登録した別名が入った会議のメモだけを開きます。それ以外は中身を開きません。",
|
|
276
|
+
"tenancyText": "プロジェクトごとに接続します。",
|
|
277
|
+
"customer": {
|
|
278
|
+
"approverLabel": "お願いする方",
|
|
279
|
+
"approverText": "会議の予定を作る方",
|
|
280
|
+
"grants": [],
|
|
281
|
+
"share": "会議の予定をプロジェクトのカレンダーに入れる",
|
|
282
|
+
"keyOnly": null,
|
|
283
|
+
"notRequested": []
|
|
284
|
+
},
|
|
285
|
+
"cortexAccount": {
|
|
286
|
+
"text": "Cortex のサービス用アカウント(Google Drive と同じアカウント)が、プロジェクトのカレンダーを用意し、会議のメモを読むために持つ権限です。",
|
|
287
|
+
"grants": [
|
|
288
|
+
{
|
|
289
|
+
"spell": "https://www.googleapis.com/auth/drive.readonly",
|
|
290
|
+
"access": "read",
|
|
291
|
+
"accessText": "読む",
|
|
292
|
+
"purpose": "会議のメモを探して読む"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"spell": "https://www.googleapis.com/auth/documents.readonly",
|
|
296
|
+
"access": "read",
|
|
297
|
+
"accessText": "読む",
|
|
298
|
+
"purpose": "現在は使っていません"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"spell": "https://www.googleapis.com/auth/calendar",
|
|
302
|
+
"access": "write",
|
|
303
|
+
"accessText": "書く",
|
|
304
|
+
"purpose": "プロジェクトのカレンダーを作って共有し、会議の予定に Cortex のアカウントを出席者として加える(通知は送りません)"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"spell": "https://www.googleapis.com/auth/script.external_request",
|
|
308
|
+
"access": "write",
|
|
309
|
+
"accessText": "書く",
|
|
310
|
+
"purpose": "読んだメモを Cortex の記録へ送る"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"spell": "https://www.googleapis.com/auth/script.scriptapp",
|
|
314
|
+
"access": "write",
|
|
315
|
+
"accessText": "書く",
|
|
316
|
+
"purpose": "定時の処理を設定する"
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
},
|
|
320
|
+
"delegated": null,
|
|
321
|
+
"note": null
|
|
322
|
+
}
|
|
323
|
+
]
|
|
324
|
+
};
|
package/dist/rederivation.js
CHANGED
|
@@ -46,6 +46,7 @@ const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
|
46
46
|
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
47
47
|
const node_path_1 = __importDefault(require("node:path"));
|
|
48
48
|
const gray_matter_1 = __importDefault(require("gray-matter"));
|
|
49
|
+
const confirmed_1 = require("./confirmed");
|
|
49
50
|
/** 帳簿の版(違ったら空として扱う・engine `LEDGER_VERSION`) */
|
|
50
51
|
exports.LEDGER_VERSION = 1;
|
|
51
52
|
/** 帯を出す区画。**用語とメンバーだけ**(Decisions は積み上げ・Rules は便 13) */
|
|
@@ -63,19 +64,21 @@ const MIN_ELAPSED_DAYS = 0.5;
|
|
|
63
64
|
* その記録が「確認済み」か。**「AI生成・未確認」の札(`render.ts` の `draftBadge`)の裏返し**
|
|
64
65
|
* ——札が出ない記録が確認済み側(#667 のオーナー決定「確認済みは `status` だけで判断する」)。
|
|
65
66
|
*
|
|
66
|
-
* 🔴
|
|
67
|
+
* 🔴 **口 `confirmed.ts` の `!machineOwned` そのもの**(#699 F2b・機械が触る前の守りは `!machineOwned`)。
|
|
68
|
+
* 札も同じ口(`machineOwned`・完全一致)に聞くので、ここで trim も小文字化もしない。
|
|
67
69
|
* 1 文字でも緩めると「札は出ないのに未確認扱い」「札は出るのに確認済み扱い」が生まれる。
|
|
68
70
|
* `status` 欄が無い記録(`rstatus` が `""`)と `superseded` は**確認済み側**——札が出ないから。
|
|
69
71
|
*
|
|
70
72
|
* 🔴 **渡すのは `String()` 済みの `rstatus` だけ**(`build.ts:1048`)。生の frontmatter
|
|
71
|
-
* (`rec.fm.status`)を渡さない——YAML
|
|
73
|
+
* (`rec.fm.status`)を渡さない——YAML は真偽値も配列も返すので、札と同じ文字列にならない
|
|
74
|
+
* (口は文字列・null・undefined のほかを投げる)。
|
|
72
75
|
*
|
|
73
76
|
* ⚠️ **「確認済み」の門はもう 1 つある。** 削除の口の出し分けは `render.ts` の
|
|
74
|
-
* `humanConfirmedStatus
|
|
75
|
-
*
|
|
77
|
+
* `humanConfirmedStatus`(口の `humanOwned`・区画の語彙)で、`""` の扱いがここと逆
|
|
78
|
+
* (確認済みとして**扱う**側は `humanOwned`、機械が触る前の**守り**は `!machineOwned` という割り当て)。
|
|
76
79
|
*/
|
|
77
|
-
function isConfirmedStatus(status) {
|
|
78
|
-
return
|
|
80
|
+
function isConfirmedStatus(status, recordPath = null) {
|
|
81
|
+
return !(0, confirmed_1.machineOwned)(recordPath, status);
|
|
79
82
|
}
|
|
80
83
|
const emptyLedgers = () => ({
|
|
81
84
|
confirmation: new Map(),
|
|
@@ -387,7 +390,7 @@ function contentShaOf(raw) {
|
|
|
387
390
|
* 「帳簿に人が確認した版の指紋が無い」記録だけ(`attachRederived`)。
|
|
388
391
|
*/
|
|
389
392
|
function rederivedContentKey(recordPath, status, ledgers) {
|
|
390
|
-
if (!isConfirmedStatus(status))
|
|
393
|
+
if (!isConfirmedStatus(status, recordPath))
|
|
391
394
|
return null;
|
|
392
395
|
if (!BAND_SECTION_RE.test(recordPath))
|
|
393
396
|
return null;
|
|
@@ -489,8 +492,8 @@ function rederivedBandOf(recordPath, status, ledgers, nowShaOf, verdict, miss) {
|
|
|
489
492
|
* 「上に来るものが重要」でなければ、その仕組みが成り立たない
|
|
490
493
|
*
|
|
491
494
|
* 🔴 **`isConfirmedStatus` は消さない**——このファイルの**印の門(`rederivedContentKey`)と
|
|
492
|
-
* 帯の門(`rederivedBandOf`)**が使う。`render.ts`
|
|
493
|
-
*
|
|
495
|
+
* 帯の門(`rederivedBandOf`)**が使う。`render.ts` の札は同じ口(`confirmed.ts` の `machineOwned`)を
|
|
496
|
+
* **流し込んだ別の実体**で、2 つが一致していることは `rederived-mark-ui.test.mjs` の `[札との一致]` が見る。
|
|
494
497
|
* ここで変えるのは**並びだけ**で、札・帯・印・プロンプトに渡る材料は 1 つも変えていない。
|
|
495
498
|
*
|
|
496
499
|
* - 活性度を持つレコードが 1 件も無い区画は **1 バイトも触らない**(帳簿が無い案件は従来順)
|
package/dist/render.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.INGEST_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = void 0;
|
|
3
|
+
exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.INGEST_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = exports.PERMISSION_WIRING = void 0;
|
|
4
4
|
exports.renderSite = renderSite;
|
|
5
5
|
exports.renderSiteWithAssets = renderSiteWithAssets;
|
|
6
6
|
exports.viewerAssetSources = viewerAssetSources;
|
|
7
7
|
exports.viewerAssetNames = viewerAssetNames;
|
|
8
|
+
exports.permissionToolId = permissionToolId;
|
|
8
9
|
exports.isRepositorySlug = isRepositorySlug;
|
|
9
10
|
exports.devRepositoryRefs = devRepositoryRefs;
|
|
10
11
|
exports.claudeCodeRepositories = claudeCodeRepositories;
|
|
@@ -79,11 +80,15 @@ const node_crypto_1 = require("node:crypto");
|
|
|
79
80
|
const google_chat_guide_images_1 = require("./google-chat-guide-images");
|
|
80
81
|
const meet_guide_images_1 = require("./meet-guide-images");
|
|
81
82
|
const figma_guide_images_1 = require("./figma-guide-images");
|
|
83
|
+
// 「人が確認したか/機械が触ってよいか」の口(#699 F2b)。正本は engine の confirmed.mjs・判定は流し込むだけ
|
|
84
|
+
const confirmed_1 = require("./confirmed");
|
|
82
85
|
// フロントマターの「つながっている ID」の欄名(#686)。**綴りはここ 1 本**——読む側(画面)も
|
|
83
86
|
// 書く側も同じ定数を引く(欄名は engine 側でまだ確定していない)
|
|
84
87
|
// `<種類>:<値>` の形(#686 C4)。名寄せの提案を「同じ人ですか」の問いに変えてよいかの門は
|
|
85
88
|
// この形だけで見分ける。綴りを画面側に写さず、正本(external-id.ts)から流し込む
|
|
86
89
|
const external_id_1 = require("./external-id");
|
|
90
|
+
// 操作タブの[権限](#841)。生成物で、正本は infra/lambda/shared/permissions.mjs
|
|
91
|
+
const permissions_data_1 = require("./permissions-data");
|
|
87
92
|
// 操作タブの仮置き(#388)。判定の実体はあちら——ここは流し込みと配線だけ
|
|
88
93
|
const pending_ops_1 = require("./pending-ops");
|
|
89
94
|
/**
|
|
@@ -242,6 +247,26 @@ const ADAPTER_CATALOG = {
|
|
|
242
247
|
// 能力の並びを1箇所で決めるため
|
|
243
248
|
"ハーネス連携": [],
|
|
244
249
|
};
|
|
250
|
+
/**
|
|
251
|
+
* [権限](#841): viewer のアダプタ id → 権限の宣言(infra/lambda/shared/permissions.mjs)の id。
|
|
252
|
+
*
|
|
253
|
+
* 綴りが違うのは Google の 2 つだけで、**載っていない id は同じ綴り**。表は 1 枚だけ置く
|
|
254
|
+
* (クライアント側へは値を流し込み、引き方は下の permissionToolId を toString() で共有する)。
|
|
255
|
+
* ⚠ 定数そのものを export しない(CLAUDE_CODE_HOME の項と同じ理由。流し込んだ先で exports を踏む)
|
|
256
|
+
*/
|
|
257
|
+
const PERMISSION_TOOL_ID = {
|
|
258
|
+
"google-meet": "meet",
|
|
259
|
+
"google-drive": "drive",
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* アダプタ id → 宣言の id(#841)。**クロージャを持たない純関数に保つ**(CLIENT_JS へ
|
|
263
|
+
* toString() で流し込む。参照してよいのは同じ名前で流し込む PERMISSION_TOOL_ID だけ)
|
|
264
|
+
*/
|
|
265
|
+
function permissionToolId(adapter) {
|
|
266
|
+
return Object.prototype.hasOwnProperty.call(PERMISSION_TOOL_ID, adapter) ? PERMISSION_TOOL_ID[adapter] : adapter;
|
|
267
|
+
}
|
|
268
|
+
/** 能力→アダプタの表を**テストから読むための入れ物**(CLAUDE_CODE_LINK と同じ作法・#841) */
|
|
269
|
+
exports.PERMISSION_WIRING = { adapterCatalog: ADAPTER_CATALOG };
|
|
245
270
|
/**
|
|
246
271
|
* 導出された状態 → 画面に出す文言。**マップとタイルで同じ語を使う。**
|
|
247
272
|
*
|
|
@@ -5678,6 +5703,33 @@ button.ops-card-btn:disabled { opacity: .6; cursor: default; }
|
|
|
5678
5703
|
color: var(--muted); padding: 2px 6px; border-radius: 6px;
|
|
5679
5704
|
}
|
|
5680
5705
|
.modal-x:hover { background: var(--bg); color: var(--text); }
|
|
5706
|
+
/* [権限](#841)。見出し行は space-between なので、[権限]と ✕ を右側の箱にまとめる */
|
|
5707
|
+
.modal-head-side { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; }
|
|
5708
|
+
.modal-perm {
|
|
5709
|
+
border: 1px solid var(--border); background: var(--surface); color: var(--muted); cursor: pointer;
|
|
5710
|
+
font-size: 12px; font-weight: 600; line-height: 1.4; padding: 3px 10px; border-radius: 999px;
|
|
5711
|
+
}
|
|
5712
|
+
.modal-perm:hover { color: var(--text); border-color: var(--muted); }
|
|
5713
|
+
.modal-perm[aria-expanded="true"] { background: var(--accent-weak); color: var(--accent); border-color: var(--accent); }
|
|
5714
|
+
.perm-sec {
|
|
5715
|
+
border: 1px solid var(--border); border-radius: 10px; background: var(--bg);
|
|
5716
|
+
padding: 12px 14px; display: flex; flex-direction: column; gap: 12px;
|
|
5717
|
+
}
|
|
5718
|
+
.perm-sec[hidden] { display: none; }
|
|
5719
|
+
.perm-tool { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
5720
|
+
.perm-tool + .perm-tool { border-top: 1px solid var(--border); padding-top: 12px; }
|
|
5721
|
+
.perm-h { font-size: 14px; font-weight: 700; margin: 0; color: var(--text); }
|
|
5722
|
+
.perm-h4 { font-size: 13px; font-weight: 700; margin: 6px 0 0; color: var(--text); }
|
|
5723
|
+
.perm-p { font-size: 13px; line-height: 1.7; margin: 0; color: var(--text); }
|
|
5724
|
+
.perm-p code, .perm-table code {
|
|
5725
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; overflow-wrap: anywhere;
|
|
5726
|
+
}
|
|
5727
|
+
.perm-table-wrap { overflow-x: auto; }
|
|
5728
|
+
.perm-table { border-collapse: collapse; width: 100%; font-size: 12.5px; }
|
|
5729
|
+
.perm-table caption { text-align: left; font-weight: 700; font-size: 12.5px; color: var(--muted); padding-bottom: 4px; }
|
|
5730
|
+
.perm-table th, .perm-table td { border-bottom: 1px solid var(--border); padding: 5px 6px; text-align: left; vertical-align: top; line-height: 1.6; }
|
|
5731
|
+
.perm-table th { color: var(--muted); font-weight: 600; white-space: nowrap; }
|
|
5732
|
+
.perm-access { white-space: nowrap; }
|
|
5681
5733
|
.modal-desc { font-size: 13px; color: var(--muted); line-height: 1.7; margin: 0; }
|
|
5682
5734
|
.modal-field { display: flex; flex-direction: column; gap: 4px; }
|
|
5683
5735
|
.modal-field-label { font-size: 12px; font-weight: 600; color: var(--muted); }
|
|
@@ -5877,10 +5929,22 @@ const CLIENT_JS = `
|
|
|
5877
5929
|
// 直列化した先(ここ)で undefined を参照して壊れる。
|
|
5878
5930
|
var CAP_OTHER = ${JSON.stringify(CAP_OTHER)};
|
|
5879
5931
|
var ADAPTER_CATALOG = ${JSON.stringify(ADAPTER_CATALOG)};
|
|
5932
|
+
// [権限](#841)。中身は生成物 permissions-data.ts の値そのもの(公開ページと同じ写し)。
|
|
5933
|
+
// 小なり記号だけ逃がす(埋め込み形は script 要素の中なので、閉じタグの綴りを作らせない)
|
|
5934
|
+
var PERMISSION_TOOL_ID = ${JSON.stringify(PERMISSION_TOOL_ID)};
|
|
5935
|
+
${permissionToolId.toString()}
|
|
5936
|
+
var PERMISSIONS_DATA = ${JSON.stringify(permissions_data_1.PERMISSIONS_DATA).replace(/</g, "\\u003c")};
|
|
5880
5937
|
var CAP_STATE_TEXT = ${JSON.stringify(CAP_STATE_TEXT)};
|
|
5881
5938
|
var CAP_STATE_MARK = ${JSON.stringify(CAP_STATE_MARK)};
|
|
5882
5939
|
${externalCapability.toString()}
|
|
5883
5940
|
${sourceAdapter.toString()}
|
|
5941
|
+
// 「人が確認したか/機械が触ってよいか」の口(#699 F2b・正本は engine の confirmed.mjs・写しは confirmed.ts)。
|
|
5942
|
+
// 札・[確認済みにする]・「未確認のみ」・削除の可否は**全部これに聞く**(rstatus と直接比べない)
|
|
5943
|
+
${confirmed_1.sectionOfPath.toString()}
|
|
5944
|
+
${confirmed_1.confirmedStatusesOf.toString()}
|
|
5945
|
+
${confirmed_1.confirmedStatusChecked.toString()}
|
|
5946
|
+
${confirmed_1.machineOwned.toString()}
|
|
5947
|
+
${confirmed_1.humanOwned.toString()}
|
|
5884
5948
|
// Claude Code への導線(#404)。**組み立ての正本は上の claudeCodeUrl だけ。**
|
|
5885
5949
|
// 🔴 この定数行を落とすと、流し込んだ関数が未定義の CLAUDE_CODE_HOME を参照して
|
|
5886
5950
|
// 画面のスクリプトが丸ごと死ぬ(定数は export できないので、値はここで注入する)
|
|
@@ -6290,7 +6354,7 @@ ${calendarAddUrl.toString()}
|
|
|
6290
6354
|
}
|
|
6291
6355
|
// 未確認(status: draft = AI生成・人間未確認)の印。Gold層の4型(決定事項・用語集・メンバー・ルール)共通で、active・status無しには何も出さない
|
|
6292
6356
|
function draftBadge(rec) {
|
|
6293
|
-
return rec && rec.rstatus
|
|
6357
|
+
return rec && machineOwned(confirmPathOf(rec), rec.rstatus) ? el("span", { class: "badge draft", text: "AI生成・未確認" }) : null;
|
|
6294
6358
|
}
|
|
6295
6359
|
// 直近のGold昇格で作られたレコードの印。レコードの date は決定日なので、日付順に並べても
|
|
6296
6360
|
// 「今回の実行で何が増えたか」は分からない。ビルド時に git から判定した結果(isLatest)を出す
|
|
@@ -6307,7 +6371,7 @@ ${calendarAddUrl.toString()}
|
|
|
6307
6371
|
// 同じ場面の同じ判断なので、口を離さない。**この帯は draft にしか出ない**ので、
|
|
6308
6372
|
// 人が確認済みにした記録には削除の口ごと現れない(守りはサーバ側にもある)。
|
|
6309
6373
|
function draftNote(rec) {
|
|
6310
|
-
if (!rec || rec.rstatus
|
|
6374
|
+
if (!rec || !machineOwned(confirmPathOf(rec), rec.rstatus)) return null;
|
|
6311
6375
|
var path = promotableRecordPath(rec);
|
|
6312
6376
|
var note = el("div", { class: "draft-note" }, [
|
|
6313
6377
|
el("span", { text: "⚠️" }),
|
|
@@ -6607,6 +6671,8 @@ ${calendarAddUrl.toString()}
|
|
|
6607
6671
|
// 決定以外の一覧(タブを移っても絞り込みは残る)では絞らない——全件が消えないように
|
|
6608
6672
|
if (listState.status === RETIRED_FILTER) return rec && rec._kind === "decision" ? isRetired(rec) : true;
|
|
6609
6673
|
if (Object.prototype.hasOwnProperty.call(FOLLOWUP_FILTER_LABEL, listState.status)) return withFollowup ? matchesFollowupFilter(rec, listState.status) : true;
|
|
6674
|
+
// 「未確認のみ」は口に聞く(札と同じ答え)。それ以外の値は従来どおりの素の比較
|
|
6675
|
+
if (listState.status === "draft") return machineOwned(confirmPathOf(rec), rec.rstatus);
|
|
6610
6676
|
return rec.rstatus === listState.status;
|
|
6611
6677
|
}
|
|
6612
6678
|
/* -------------------------------------------------------------------------
|
|
@@ -6962,7 +7028,7 @@ ${calendarAddUrl.toString()}
|
|
|
6962
7028
|
|
|
6963
7029
|
// 未確認が1件も無い一覧では絞り込みを出さない
|
|
6964
7030
|
function hasUnverified(items) {
|
|
6965
|
-
return (items || []).some(function (r) { return r.rstatus
|
|
7031
|
+
return (items || []).some(function (r) { return machineOwned(confirmPathOf(r), r.rstatus); });
|
|
6966
7032
|
}
|
|
6967
7033
|
|
|
6968
7034
|
// ---------- AI導線(Gold層への人間の書き込み口。claude.ai/code へのディープリンク) ----------
|
|
@@ -7060,6 +7126,18 @@ ${calendarAddUrl.toString()}
|
|
|
7060
7126
|
var m = rec.editUrl.match(/\\/edit\\/[^/]+\\/(.+)$/);
|
|
7061
7127
|
return m ? decodeURIComponent(m[1]) : "";
|
|
7062
7128
|
}
|
|
7129
|
+
/**
|
|
7130
|
+
* 口(machineOwned・humanOwned)へ渡す path。記録の path が引ければそれ、引けなければ
|
|
7131
|
+
* **区画だけを 「Cortex/<区画>/」 の形で伝える**(engine の supersedes-index が決定の区画で聞くのと同じ意味)。
|
|
7132
|
+
* 区画も分からない記録(Home など)は null(口は区画不明として答える)
|
|
7133
|
+
*/
|
|
7134
|
+
function confirmPathOf(rec) {
|
|
7135
|
+
var p = repoPathOf(rec);
|
|
7136
|
+
if (p) return p;
|
|
7137
|
+
if (rec && rec._kind === "decision") return "Cortex/Decisions/";
|
|
7138
|
+
if (rec && rec._kind === "generic" && rec._section) return "Cortex/" + rec._section + "/";
|
|
7139
|
+
return null;
|
|
7140
|
+
}
|
|
7063
7141
|
// Gold型(ディレクトリ名)→ 書き込みプロンプトの対応。決定事項/用語集/メンバー/ルールの4型のみ。
|
|
7064
7142
|
// レポート等スキルの無いディレクトリは対象外(ボタンを出さない)。
|
|
7065
7143
|
// プロンプトには規約を書き写さない。各区画の README.md(規約)と template.md(雛形)をAIに読ませ、
|
|
@@ -7286,7 +7364,7 @@ ${calendarAddUrl.toString()}
|
|
|
7286
7364
|
// ——のではなく**明示的に据え置きを指示する**。指示が無いと、AIが「AIが書いたのだから
|
|
7287
7365
|
// draft にすべき」と判断して人間の確認を取り消す形になる(実際に起こりうる読み方)
|
|
7288
7366
|
lines.push(
|
|
7289
|
-
rec.rstatus
|
|
7367
|
+
machineOwned(confirmPathOf(rec), rec.rstatus)
|
|
7290
7368
|
? "あなた(人間)が内容を確認して編集したので、status が draft なら active に上げてください。"
|
|
7291
7369
|
: "この記録は確認済み(status: active)です。**適用後も status は active のままにしてください**(draft に戻さないでください)。",
|
|
7292
7370
|
);
|
|
@@ -7575,7 +7653,7 @@ ${calendarAddUrl.toString()}
|
|
|
7575
7653
|
/** どちらのボタンにも付ける締め(status の据え置き/昇格と、書き込む前の承認) */
|
|
7576
7654
|
function ipTail(rec) {
|
|
7577
7655
|
return [
|
|
7578
|
-
rec.rstatus
|
|
7656
|
+
machineOwned(confirmPathOf(rec), rec.rstatus)
|
|
7579
7657
|
? "あなた(人間)が内容を確認して編集したので、status が draft なら active に上げてください。"
|
|
7580
7658
|
: "この記録は確認済み(status: active)です。**適用後も status は active のままにしてください**(draft に戻さないでください)。",
|
|
7581
7659
|
"書き込む前に、変更内容を提示して承認を得てください。",
|
|
@@ -11636,6 +11714,151 @@ ${calendarAddUrl.toString()}
|
|
|
11636
11714
|
return (tool && CM_TOOL_LABEL_C[tool]) || cap;
|
|
11637
11715
|
}
|
|
11638
11716
|
|
|
11717
|
+
/**
|
|
11718
|
+
* [権限](#841): 開いたモーダルがどのツールの権限を説明するか。宣言の行の配列を返す。
|
|
11719
|
+
*
|
|
11720
|
+
* 開いたタイルのアダプタ → 宣言の id(permissionToolId)。**アダプタが分からない呼び出しは
|
|
11721
|
+
* 能力のカタログ全部**——チャット(Slack / Google Chat)の混載の1枚タイルでは両方を出す。
|
|
11722
|
+
* 空を返したらボタンを出さない: 能力ではない見出し(「文字起こしを手動で追加」など)・
|
|
11723
|
+
* カタログ外のアダプタ・写しに無いツール。名前の無い説明を出すより出さないほうがよい
|
|
11724
|
+
*/
|
|
11725
|
+
function permissionToolsFor(cap, adapter) {
|
|
11726
|
+
var list = ADAPTER_CATALOG[cap] || [];
|
|
11727
|
+
var ids = adapter ? (list.indexOf(adapter) >= 0 ? [adapter] : []) : list;
|
|
11728
|
+
var tools = (PERMISSIONS_DATA && PERMISSIONS_DATA.tools) || [];
|
|
11729
|
+
var out = [];
|
|
11730
|
+
ids.forEach(function (a) {
|
|
11731
|
+
var want = permissionToolId(a);
|
|
11732
|
+
for (var i = 0; i < tools.length; i++) {
|
|
11733
|
+
if (tools[i] && tools[i].id === want) { out.push(tools[i]); return; }
|
|
11734
|
+
}
|
|
11735
|
+
});
|
|
11736
|
+
return out;
|
|
11737
|
+
}
|
|
11738
|
+
|
|
11739
|
+
/**
|
|
11740
|
+
* ポータル配下(/p/<キー>/ の下)で開いているか(#379・#841)。
|
|
11741
|
+
* ヘッダーの「プロジェクト一覧へ」と[権限]の「すべてのツールの一覧」が同じ判定を引く
|
|
11742
|
+
* ——写しを 2 本にしない
|
|
11743
|
+
*/
|
|
11744
|
+
function underPortal() {
|
|
11745
|
+
return /^\\/p\\/[^/]+\\//.test(location.pathname);
|
|
11746
|
+
}
|
|
11747
|
+
|
|
11748
|
+
/** 権限の表(表記・読む/書く・何のために)。綴りは表記の列だけに閉じ込める */
|
|
11749
|
+
function permissionTable(caption, grants) {
|
|
11750
|
+
var rows = grants.map(function (g) {
|
|
11751
|
+
return el("tr", null, [
|
|
11752
|
+
el("td", null, [el("code", { text: g.spell })]),
|
|
11753
|
+
el("td", { class: "perm-access", text: g.accessText }),
|
|
11754
|
+
el("td", { text: g.purpose }),
|
|
11755
|
+
]);
|
|
11756
|
+
});
|
|
11757
|
+
return el("div", { class: "perm-table-wrap" }, [
|
|
11758
|
+
el("table", { class: "perm-table" }, [
|
|
11759
|
+
el("caption", { text: caption }),
|
|
11760
|
+
el("thead", null, [el("tr", null, [
|
|
11761
|
+
el("th", { scope: "col", text: "表記" }),
|
|
11762
|
+
el("th", { scope: "col", text: "読む/書く" }),
|
|
11763
|
+
el("th", { scope: "col", text: "何のために" }),
|
|
11764
|
+
])]),
|
|
11765
|
+
el("tbody", null, rows),
|
|
11766
|
+
]),
|
|
11767
|
+
]);
|
|
11768
|
+
}
|
|
11769
|
+
|
|
11770
|
+
/**
|
|
11771
|
+
* [権限]の節(#841)。**押したときに初めて組み立てる**——初めから DOM に置くと、
|
|
11772
|
+
* モーダルの中身を取り出して調べる既存の検査(expandAllCaps)の文字列に混ざる。
|
|
11773
|
+
*
|
|
11774
|
+
* 公開ページの冒頭の説明文は出さない(チャットのモーダルには開示文が既にある・繰り返さない)。
|
|
11775
|
+
* 「以前に接続した場合は…再承認…」の一文も公開ページ側だけ
|
|
11776
|
+
*/
|
|
11777
|
+
function buildPermissionSection(tools) {
|
|
11778
|
+
var sec = el("section", { class: "perm-sec" });
|
|
11779
|
+
tools.forEach(function (t) {
|
|
11780
|
+
var box = el("div", { class: "perm-tool", "data-perm-tool": t.id });
|
|
11781
|
+
box.appendChild(el("h3", { class: "perm-h", text: t.tool + " で Cortex が求める権限" }));
|
|
11782
|
+
[t.how, t.reach, t.tenancyText, t.note].forEach(function (line) {
|
|
11783
|
+
if (line) box.appendChild(el("p", { class: "perm-p", text: line }));
|
|
11784
|
+
});
|
|
11785
|
+
var cu = t.customer;
|
|
11786
|
+
if (cu) {
|
|
11787
|
+
if (cu.grants && cu.grants.length) {
|
|
11788
|
+
box.appendChild(permissionTable("お客様に許可していただく権限", cu.grants));
|
|
11789
|
+
}
|
|
11790
|
+
if (cu.approverText) box.appendChild(el("p", { class: "perm-p", text: cu.approverLabel + ": " + cu.approverText }));
|
|
11791
|
+
if (cu.share) box.appendChild(el("p", { class: "perm-p", text: "お願いすること: " + cu.share }));
|
|
11792
|
+
if (cu.keyOnly && cu.keyOnly.text) box.appendChild(el("p", { class: "perm-p", text: cu.keyOnly.text }));
|
|
11793
|
+
if (cu.notRequested && cu.notRequested.length) {
|
|
11794
|
+
var nr = el("p", { class: "perm-p" }, [document.createTextNode("次の権限は求めません: ")]);
|
|
11795
|
+
cu.notRequested.forEach(function (n, i) {
|
|
11796
|
+
if (i) nr.appendChild(document.createTextNode("、"));
|
|
11797
|
+
nr.appendChild(document.createTextNode(n.label + "("));
|
|
11798
|
+
nr.appendChild(el("code", { text: n.spell }));
|
|
11799
|
+
nr.appendChild(document.createTextNode(")"));
|
|
11800
|
+
});
|
|
11801
|
+
box.appendChild(nr);
|
|
11802
|
+
}
|
|
11803
|
+
}
|
|
11804
|
+
var ca = t.cortexAccount;
|
|
11805
|
+
if (ca) {
|
|
11806
|
+
box.appendChild(el("h4", { class: "perm-h4", text: "Cortex 側のアカウントが持つ権限" }));
|
|
11807
|
+
if (ca.text) box.appendChild(el("p", { class: "perm-p", text: ca.text }));
|
|
11808
|
+
if (ca.grants && ca.grants.length) box.appendChild(permissionTable("Cortex 側のアカウントが持つ権限", ca.grants));
|
|
11809
|
+
}
|
|
11810
|
+
var dg = t.delegated;
|
|
11811
|
+
if (dg) {
|
|
11812
|
+
box.appendChild(el("h4", { class: "perm-h4", text: "ご本人が AI の道具からつなぐとき" }));
|
|
11813
|
+
if (dg.text) box.appendChild(el("p", { class: "perm-p", text: dg.text }));
|
|
11814
|
+
if (dg.grants && dg.grants.length) box.appendChild(permissionTable("ご本人が AI の道具からつなぐとき", dg.grants));
|
|
11815
|
+
}
|
|
11816
|
+
sec.appendChild(box);
|
|
11817
|
+
});
|
|
11818
|
+
// 一覧(ポータルの公開ページ)はポータル配下でしか辿れない。Amplify や手元では出さない
|
|
11819
|
+
if (underPortal()) {
|
|
11820
|
+
sec.appendChild(el("p", { class: "perm-p perm-all" }, [
|
|
11821
|
+
el("a", { href: "/legal/security", target: "_blank", rel: "noopener", text: "すべてのツールの一覧 ↗" }),
|
|
11822
|
+
]));
|
|
11823
|
+
}
|
|
11824
|
+
return sec;
|
|
11825
|
+
}
|
|
11826
|
+
|
|
11827
|
+
/**
|
|
11828
|
+
* モーダルの見出し行に[権限]を差し込む(#841)。makeModal の引数は増やさない。
|
|
11829
|
+
* 見出し行は space-between なので、子を 3 つ並べると見出しが真ん中へ寄る——
|
|
11830
|
+
* [権限]と ✕ を右側の箱にまとめる。✕ へのフォーカス(showModal)はそのまま
|
|
11831
|
+
*/
|
|
11832
|
+
var permSeq = 0;
|
|
11833
|
+
function attachPermissionButton(m, tools) {
|
|
11834
|
+
var head = m.modal.querySelector(".modal-head");
|
|
11835
|
+
var x = head && head.querySelector(".modal-x");
|
|
11836
|
+
if (!head || !x) return;
|
|
11837
|
+
permSeq += 1;
|
|
11838
|
+
var secId = "perm-sec-" + permSeq;
|
|
11839
|
+
var btn = el("button", {
|
|
11840
|
+
class: "modal-perm", type: "button", "aria-expanded": "false", text: "権限",
|
|
11841
|
+
});
|
|
11842
|
+
var side = el("div", { class: "modal-head-side" });
|
|
11843
|
+
head.insertBefore(side, x);
|
|
11844
|
+
side.appendChild(btn);
|
|
11845
|
+
side.appendChild(x);
|
|
11846
|
+
var sec = null;
|
|
11847
|
+
btn.addEventListener("click", function () {
|
|
11848
|
+
var opening = btn.getAttribute("aria-expanded") !== "true";
|
|
11849
|
+
if (opening && !sec) {
|
|
11850
|
+
sec = buildPermissionSection(tools);
|
|
11851
|
+
sec.id = secId;
|
|
11852
|
+
// 指す先ができてから結ぶ(無い id を指させない)
|
|
11853
|
+
btn.setAttribute("aria-controls", secId);
|
|
11854
|
+
// 本文の先頭(見出し行の直後)
|
|
11855
|
+
m.modal.insertBefore(sec, head.nextSibling);
|
|
11856
|
+
}
|
|
11857
|
+
if (sec) sec.hidden = !opening;
|
|
11858
|
+
btn.setAttribute("aria-expanded", opening ? "true" : "false");
|
|
11859
|
+
});
|
|
11860
|
+
}
|
|
11861
|
+
|
|
11639
11862
|
/**
|
|
11640
11863
|
* @param noteIsUnset その説明が「未接続(unset)」由来か。
|
|
11641
11864
|
* 🔴 **反映待ちで抑制してよいのはこれだけ。** この経路は「未接続」「OFFに設定されて
|
|
@@ -11651,6 +11874,9 @@ ${calendarAddUrl.toString()}
|
|
|
11651
11874
|
noteOpenCap(cap);
|
|
11652
11875
|
// 見出しは押したタイルのツール名(Backlog・Google ドライブ…)。capToolLabel の説明を参照
|
|
11653
11876
|
var m = makeModal(capToolLabel(cap, adapter), true);
|
|
11877
|
+
// [権限](#841)。ツールが決まらない・写しに無いときは出さない(permissionToolsFor の項)
|
|
11878
|
+
var permTools = permissionToolsFor(cap, adapter);
|
|
11879
|
+
if (permTools.length) attachPermissionButton(m, permTools);
|
|
11654
11880
|
// **何が読み取られるのかを必ず書く。** 一覧に並ぶものと読み取られるものの関係は、
|
|
11655
11881
|
// 書いていないと分からない(チャットは「載っていれば全部」、開発はリポジトリ単位でOFFできる)
|
|
11656
11882
|
if (cap === "チャット") {
|
|
@@ -18815,14 +19041,13 @@ ${calendarAddUrl.toString()}
|
|
|
18815
19041
|
if (meta && meta.parentNode) meta.parentNode.insertBefore(stale, meta.nextSibling);
|
|
18816
19042
|
}
|
|
18817
19043
|
/**
|
|
18818
|
-
*
|
|
18819
|
-
*
|
|
18820
|
-
* inactive
|
|
19044
|
+
* 人の確認印が付いた記録か。**口の humanOwned に聞く**(#699 F2b・intake の deletableRecordStatus も
|
|
19045
|
+
* 同じ口の写しに聞く)。区画の語彙から draft を除いた値(決定・ルール active/用語 active・superseded/
|
|
19046
|
+
* 関係者 active・inactive)。ここは「押しても断られるボタンを出さない」ための
|
|
18821
19047
|
* 出し分けで、**守りはサーバ側**(promote の PROMOTABLE_PATH と同じ関係)。
|
|
18822
19048
|
*/
|
|
18823
|
-
function humanConfirmedStatus(rec) {
|
|
18824
|
-
|
|
18825
|
-
return s === "active" || s === "superseded" || s === "inactive";
|
|
19049
|
+
function humanConfirmedStatus(rec, path) {
|
|
19050
|
+
return !!rec && humanOwned(path || confirmPathOf(rec), rec.rstatus);
|
|
18826
19051
|
}
|
|
18827
19052
|
/**
|
|
18828
19053
|
* 削除の口(ボタンと、その結果を出す行)。**置き場は呼び出し側が決める。**
|
|
@@ -18836,9 +19061,11 @@ ${calendarAddUrl.toString()}
|
|
|
18836
19061
|
* - 詳細ページの操作列 …… 警告帯が出ない記録。置き場が他に無いのでここに寄せる
|
|
18837
19062
|
*/
|
|
18838
19063
|
function buildDeleteControls(rec) {
|
|
18839
|
-
|
|
19064
|
+
// 🔴 path を先に引く——削除できる記録は path が必ず引けるので、口には区画入りの path で聞ける
|
|
19065
|
+
//(intake と同じ答え。区画の分からない path で聞くと決定の superseded まで確認済みに数える)
|
|
18840
19066
|
var path = deletableRecordPath(rec);
|
|
18841
19067
|
if (!path) return null;
|
|
19068
|
+
if (humanConfirmedStatus(rec, path)) return null;
|
|
18842
19069
|
// **語尾の「…」は 2026-08-29 のオーナーFBで外した。** 二択の相方(「確認済みにする」)と
|
|
18843
19070
|
// 語の形が揃わず、片方だけが下書きのように見えていた。押した先に確認モーダルが必ず出ることは
|
|
18844
19071
|
// 語尾ではなく操作そのもの(confirmDanger を await する下の行)で担保している
|
|
@@ -18860,7 +19087,7 @@ ${calendarAddUrl.toString()}
|
|
|
18860
19087
|
* 見た目は控えめな文字のボタンのままにして、隣の実体のあるボタンと重さを分ける。
|
|
18861
19088
|
*/
|
|
18862
19089
|
function attachDeleteToEditRow(rec, editRow) {
|
|
18863
|
-
if (!rec || rec.rstatus
|
|
19090
|
+
if (!rec || machineOwned(confirmPathOf(rec), rec.rstatus)) return null;
|
|
18864
19091
|
var del = buildDeleteControls(rec);
|
|
18865
19092
|
if (!del) return null;
|
|
18866
19093
|
editRow.appendChild(del.btn);
|
|
@@ -20263,7 +20490,8 @@ ${calendarAddUrl.toString()}
|
|
|
20263
20490
|
*/
|
|
20264
20491
|
(function linkTitleToPortal() {
|
|
20265
20492
|
var a = document.getElementById("home-link");
|
|
20266
|
-
|
|
20493
|
+
// 判定は underPortal([権限]の一覧リンクと同じ 1 本・#841)
|
|
20494
|
+
if (!a || !underPortal()) return;
|
|
20267
20495
|
a.setAttribute("href", "/");
|
|
20268
20496
|
a.setAttribute("title", "プロジェクト一覧へ");
|
|
20269
20497
|
a.classList.add("site-title-portal");
|
package/dist/supersedes.js
CHANGED
|
@@ -3,7 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.CONFIRMED_STATUS = void 0;
|
|
7
6
|
exports.decisionKeyOf = decisionKeyOf;
|
|
8
7
|
exports.decisionDateOf = decisionDateOf;
|
|
9
8
|
exports.supersedesIndex = supersedesIndex;
|
|
@@ -23,14 +22,20 @@ exports.retiredIdsOf = retiredIdsOf;
|
|
|
23
22
|
*
|
|
24
23
|
* 1. 置き換えの正本は**新しい側の `relations[supersedes]` だけ**。古い側の `superseded_by`
|
|
25
24
|
* (engine の夜間の精製が書く写し)は逆引きの材料にしない
|
|
26
|
-
* 2. **確認済み=新しい側の `status` が `active
|
|
25
|
+
* 2. **確認済み=新しい側の `status` が `active`**(口 `confirmed.ts` の `humanOwned`・決定の区画の答え)。
|
|
26
|
+
* 空・`draft`・語彙の外は「未確認」
|
|
27
27
|
* 3. **置き換えられた(もう有効でない・retired)=確認済みの置き換えが 1 本以上**
|
|
28
28
|
* 4. 置き換え先は**全部・新しい順**(決定日の降順 → ID の降順・同じ ID は 1 回)
|
|
29
29
|
* 5. 自分自身を指す・実在しない ID は数えない。古い側の `status` は問わない
|
|
30
30
|
*/
|
|
31
31
|
const node_path_1 = __importDefault(require("node:path"));
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const confirmed_1 = require("./confirmed");
|
|
33
|
+
/**
|
|
34
|
+
* 口に渡す path が無い・決定の区画の外のときの置き場(逆引きに来るのは決定だけなので、決定の区画の答えで聞く)。
|
|
35
|
+
* 🔴 区画の分からない path のまま聞くと、決定の語彙に無い `superseded`・`inactive` まで確認済みに数える
|
|
36
|
+
* (engine の `DECISIONS_ANCHOR` と同じ)
|
|
37
|
+
*/
|
|
38
|
+
const DECISIONS_ANCHOR = "Cortex/Decisions/records/";
|
|
34
39
|
/**
|
|
35
40
|
* 決定を指す文字列を突き合わせの鍵にする(`decision:` の頭・パス・`.md` を落とす)。
|
|
36
41
|
* engine は `path.basename`(Linux で走る=posix)なので、ここも posix で固定する
|
|
@@ -56,6 +61,8 @@ function normalize(d) {
|
|
|
56
61
|
key: decisionKeyOf(id),
|
|
57
62
|
date: decisionDateOf(r.date, id),
|
|
58
63
|
status: typeof r.status === "string" ? r.status.trim() : "",
|
|
64
|
+
// 口(`confirmed.ts`)へ渡すだけ(逆引きの出力には載せない・題材の契約は変わらない)
|
|
65
|
+
path: typeof r.path === "string" && r.path.includes("Cortex/Decisions/") ? r.path : DECISIONS_ANCHOR,
|
|
59
66
|
supersedes: (Array.isArray(r.supersedes) ? r.supersedes : []).map(decisionKeyOf).filter(Boolean),
|
|
60
67
|
};
|
|
61
68
|
}
|
|
@@ -91,7 +98,7 @@ function supersedesIndex(decisions) {
|
|
|
91
98
|
const entry = out.get(old.key) || { id: old.id, by: [], retired: false };
|
|
92
99
|
if (entry.by.some((x) => decisionKeyOf(x.id) === d.key))
|
|
93
100
|
continue;
|
|
94
|
-
const confirmed = d.
|
|
101
|
+
const confirmed = (0, confirmed_1.humanOwned)(d.path, d.status);
|
|
95
102
|
entry.by.push({ id: d.id, date: d.date, status: d.status, confirmed });
|
|
96
103
|
if (confirmed)
|
|
97
104
|
entry.retired = true;
|
package/package.json
CHANGED