@takagaki/cortex-decisions-viewer 0.12.64 → 0.12.66
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/README.md +1 -1
- package/dist/build.js +111 -10
- package/dist/render.js +179 -83
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ npx -y @takagaki/cortex-decisions-viewer build --out site --title "Decisions"
|
|
|
20
20
|
| `index.html` | 画面の骨組みと、埋め込みデータ(`<script type="application/json">`) |
|
|
21
21
|
| `404.html` | `index.html` と同じもの(サブパス直叩きのフォールバック) |
|
|
22
22
|
| `assets/viewer.<内容ハッシュ>.js` / `.css` | 画面のJSとCSS。内容が同じなら名前も同じ(版が同じなら全案件で同じ名前) |
|
|
23
|
-
| `bodies
|
|
23
|
+
| `bodies/decisions/<id>.html` / `bodies/home.html` / `bodies/<区画>/<id>.html` | レコードの本文(詳細を開いたときに取りに行く)。**名前は並び順ではなくレコードから決まる**(cortex-engine #382)。先頭に `<!-- cortex-body id="..." -->` の印が入り、画面は取ってきた本文がそのレコードのものか確かめる |
|
|
24
24
|
| `thumbs/*` | グラフのサムネイル画像 |
|
|
25
25
|
|
|
26
26
|
**配置は `site/` を丸ごと置く**(`index.html` だけを置いても画面は出ない)。
|
package/dist/build.js
CHANGED
|
@@ -44,8 +44,12 @@ exports.readGoldProposals = readGoldProposals;
|
|
|
44
44
|
exports.buildHomeGrounding = buildHomeGrounding;
|
|
45
45
|
exports.collectMeetings = collectMeetings;
|
|
46
46
|
exports.findBacklogDir = findBacklogDir;
|
|
47
|
+
exports.bodyFileSlug = bodyFileSlug;
|
|
48
|
+
exports.bodyStampId = bodyStampId;
|
|
49
|
+
exports.bodyStampComment = bodyStampComment;
|
|
47
50
|
exports.build = build;
|
|
48
51
|
exports.createRefResolver = createRefResolver;
|
|
52
|
+
const node_crypto_1 = require("node:crypto");
|
|
49
53
|
const node_fs_1 = require("node:fs");
|
|
50
54
|
const path = __importStar(require("node:path"));
|
|
51
55
|
const gray_matter_1 = __importDefault(require("gray-matter"));
|
|
@@ -911,6 +915,7 @@ async function parseGeneric(raw, fileName, repoRelDir, repoBaseUrl, branch) {
|
|
|
911
915
|
summary,
|
|
912
916
|
bodyHtml,
|
|
913
917
|
bodyRef: "",
|
|
918
|
+
bodyId: "",
|
|
914
919
|
searchText: toSearchText(content),
|
|
915
920
|
fileName,
|
|
916
921
|
editUrl,
|
|
@@ -1700,25 +1705,120 @@ function computeLayout(graph) {
|
|
|
1700
1705
|
}
|
|
1701
1706
|
}
|
|
1702
1707
|
/**
|
|
1703
|
-
*
|
|
1708
|
+
* 本文ファイルの名前に使える形へ畳む。
|
|
1709
|
+
*
|
|
1710
|
+
* 🔴 **畳んだときの区別に連番を使わない。** 連番は「並び順で本文を引く」形に戻る——
|
|
1711
|
+
* それが cortex-engine #382 の不具合そのもの(レコードを1件消すと、それより古い全レコードの
|
|
1712
|
+
* 本文が1つずつずれる)。畳んだ跡があるときは**元の値から決まる印**を足して区別する。
|
|
1713
|
+
* 同じ入力なら常に同じ名前になり、他のレコードが増えても消えても変わらない。
|
|
1714
|
+
*
|
|
1715
|
+
* ASCII に倒すのは配布先(S3 + CloudFront)で確かめられていない形を持ち込まないため。
|
|
1716
|
+
* 案件の id と区画名は日本語を含みうる(レコードの id はファイル名から導出されることがある)。
|
|
1717
|
+
*/
|
|
1718
|
+
function bodyFileSlug(raw) {
|
|
1719
|
+
const s = String(raw ?? "").normalize("NFC");
|
|
1720
|
+
const folded = s
|
|
1721
|
+
.replace(/[^A-Za-z0-9._-]+/g, "-")
|
|
1722
|
+
.replace(/-{2,}/g, "-")
|
|
1723
|
+
// 🔴 `..` を残さない。ポータルの門は URI に `..` を含むと無条件で 403 を返す
|
|
1724
|
+
// (classmethod-cortex-portal の viewer-gate.js)ので、本文が丸ごと読めなくなる
|
|
1725
|
+
.replace(/\.{2,}/g, ".")
|
|
1726
|
+
.replace(/^[-.]+|[-.]+$/g, "");
|
|
1727
|
+
if (s !== "" && folded === s)
|
|
1728
|
+
return s; // 畳んでいない=そのまま使える
|
|
1729
|
+
const h = shortDigest(s);
|
|
1730
|
+
return folded ? `${folded}-${h}` : h;
|
|
1731
|
+
}
|
|
1732
|
+
/** 元の値から決まる短い印(並び順に依らない区別に使う。assets のハッシュ名と同じ桁数) */
|
|
1733
|
+
function shortDigest(s) {
|
|
1734
|
+
return (0, node_crypto_1.createHash)("sha256").update(s, "utf8").digest("hex").slice(0, 8);
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* 本文がどのレコードのものかを表す印。`<kind>:<id>` の形で、
|
|
1738
|
+
* 本文ファイルの先頭コメントと inlineデータの `bodyId` の両方に同じ文字列が入る。
|
|
1739
|
+
*
|
|
1740
|
+
* `<` `>` `&` `"` を逃がすので、id に何が入っていてもコメントを閉じたり属性を割ったりしない。
|
|
1741
|
+
*
|
|
1742
|
+
* `extra`(同じ区画に id が重複したときのファイル名)を渡すと `<kind>:<id>:<extra>` になる。
|
|
1743
|
+
* 🔴 **重複しているときは印にも区別が要る。** 印が `<kind>:<id>` だけだと、重複した2件の
|
|
1744
|
+
* 一方が消えた日に、残った方の本文が**消えた方の印と一致してしまう**——古い画面に
|
|
1745
|
+
* 別レコードの本文が出る(#382 の症状そのもの)。
|
|
1746
|
+
*/
|
|
1747
|
+
function bodyStampId(kind, id, extra) {
|
|
1748
|
+
const esc = (v) => String(v ?? "")
|
|
1749
|
+
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1750
|
+
return `${esc(kind)}:${esc(id)}` + (extra ? `:${esc(extra)}` : "");
|
|
1751
|
+
}
|
|
1752
|
+
/** 本文ファイルの先頭に置く印の行(画面側の BODY_STAMP_RE と対) */
|
|
1753
|
+
function bodyStampComment(stampId) {
|
|
1754
|
+
return `<!-- cortex-body id="${stampId}" -->\n`;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* 各レコードの bodyHtml を out/bodies/... へ書き出し、bodyRef にパスを設定して
|
|
1704
1758
|
* inlineデータ上の bodyHtml を空にする(初期ロードのpayloadから本文を外す)。
|
|
1759
|
+
*
|
|
1760
|
+
* 🔴 **ファイル名は並び順ではなくレコードから決まる**(cortex-engine #382)。
|
|
1761
|
+
* 前は `bodies/<並び順の番号>.html` だった。詳細を開いた画面は埋め込まれた bodyRef を
|
|
1762
|
+
* fetch するので、**削除で番号が詰まると、開きっぱなしの画面が別レコードの本文を出す**。
|
|
1763
|
+
* 見出しはそのレコードのまま本文だけが入れ替わるので、見比べない限り誰も気づけない。
|
|
1764
|
+
*
|
|
1765
|
+
* 置き場:
|
|
1766
|
+
* 決定 bodies/decisions/<id>.html
|
|
1767
|
+
* Home bodies/home.html
|
|
1768
|
+
* 区画レコード bodies/<区画のディレクトリ名>/<id>.html
|
|
1769
|
+
*
|
|
1770
|
+
* 併せて各ファイルの先頭に印(bodyStampComment)を埋める。画面はこれを突き合わせて、
|
|
1771
|
+
* 食い違ったら本文を出さない(配布の途中・移行期・id が重複した案件の保険)。
|
|
1705
1772
|
*/
|
|
1706
1773
|
async function externalizeBodies(site, outAbs) {
|
|
1707
1774
|
const all = [
|
|
1708
|
-
...site.decisions,
|
|
1709
|
-
...
|
|
1710
|
-
...site.sections.flatMap((s) => s.records),
|
|
1775
|
+
...site.decisions.map((rec) => ({ rec, kind: "decisions", dir: "bodies/decisions" })),
|
|
1776
|
+
...site.sections.flatMap((s) => s.records.map((rec) => ({ rec, kind: s.name, dir: `bodies/${bodyFileSlug(s.name)}` }))),
|
|
1711
1777
|
];
|
|
1712
|
-
|
|
1778
|
+
const home = site.home;
|
|
1779
|
+
// **先に丸ごと消す。** 出力ディレクトリは作り直されないので、消さないと前のビルドの
|
|
1780
|
+
// 本文(番号の名前を含む)がファイルとして残る。配布先は `aws s3 sync --delete` が
|
|
1781
|
+
// 消すが、手元のプレビューでは古い本文が URL 直打ちで生き続ける(アイコンと同じ手当て)。
|
|
1782
|
+
// 🔴 **0件の回にも消す。** 早期 return より前に置くこと——全部消した案件が
|
|
1783
|
+
// 「前のビルドの本文だけが残る」状態になる(消したはずのものが読める、が一番まずい)
|
|
1784
|
+
await node_fs_1.promises.rm(path.join(outAbs, "bodies"), { recursive: true, force: true });
|
|
1785
|
+
if (all.length === 0 && !home)
|
|
1713
1786
|
return;
|
|
1714
1787
|
await node_fs_1.promises.mkdir(path.join(outAbs, "bodies"), { recursive: true });
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
await node_fs_1.promises.writeFile(path.join(outAbs, ref), rec.bodyHtml ?? "", "utf8");
|
|
1788
|
+
const write = async (rec, ref, stampId) => {
|
|
1789
|
+
await node_fs_1.promises.mkdir(path.dirname(path.join(outAbs, ref)), { recursive: true });
|
|
1790
|
+
await node_fs_1.promises.writeFile(path.join(outAbs, ref), bodyStampComment(stampId) + (rec.bodyHtml ?? ""), "utf8");
|
|
1719
1791
|
rec.bodyRef = ref;
|
|
1792
|
+
rec.bodyId = stampId;
|
|
1720
1793
|
rec.bodyHtml = ""; // inlineデータからは本文を除外(payload削減)
|
|
1721
|
-
|
|
1794
|
+
};
|
|
1795
|
+
// Home は1件だけなので固定名。区画に "home" という名前があってもディレクトリなのでぶつからない
|
|
1796
|
+
if (home)
|
|
1797
|
+
await write(home, "bodies/home.html", bodyStampId("home", home.id));
|
|
1798
|
+
// 同じ区画に同じ id のレコードが2件あるときの避け方。
|
|
1799
|
+
//
|
|
1800
|
+
// 🔴 **`-1` `-2` の連番で避けない(設計レビューの指摘)。** 連番は並び順から決まるので、
|
|
1801
|
+
// 重複した2件の一方が消えた日に、残った方が空いた番号へ繰り上がる。古い画面の bodyRef と
|
|
1802
|
+
// 印がそのまま一致してしまい、**別レコードの本文が出る**——#382 の症状が形を変えて戻る。
|
|
1803
|
+
// だから**重複している側の全員**に、そのレコードの出どころから決まる印を付ける
|
|
1804
|
+
// (誰が消えても残った側の名前は変わらない)。印にも同じ区別を入れる。
|
|
1805
|
+
//
|
|
1806
|
+
// 区別の材料は**リポジトリの中での置き場**(editUrl にパスが入っている)。今は records/ を
|
|
1807
|
+
// 掘らないのでファイル名だけでも一意だが、掘るようになった日に黙って上書きし合う形にしない。
|
|
1808
|
+
//
|
|
1809
|
+
// 大文字小文字だけ違う id も同じ組として数える。macOS のローカルビルドでは
|
|
1810
|
+
// 別名にならず黙って上書きし合うため(S3 は区別するので手元だけ壊れる、が一番気づけない)
|
|
1811
|
+
const originOf = (rec) => `${rec.editUrl ?? ""}|${rec.fileName}`;
|
|
1812
|
+
const dupCount = new Map();
|
|
1813
|
+
for (const { rec, dir } of all) {
|
|
1814
|
+
const key = `${dir}/${bodyFileSlug(rec.id)}`.toLowerCase();
|
|
1815
|
+
dupCount.set(key, (dupCount.get(key) ?? 0) + 1);
|
|
1816
|
+
}
|
|
1817
|
+
for (const { rec, kind, dir } of all) {
|
|
1818
|
+
const base = `${dir}/${bodyFileSlug(rec.id)}`;
|
|
1819
|
+
const dup = (dupCount.get(base.toLowerCase()) ?? 0) > 1;
|
|
1820
|
+
const ref = dup ? `${base}-${shortDigest(originOf(rec))}.html` : `${base}.html`;
|
|
1821
|
+
await write(rec, ref, bodyStampId(kind, rec.id, dup ? originOf(rec) : undefined));
|
|
1722
1822
|
}
|
|
1723
1823
|
}
|
|
1724
1824
|
async function build(opts) {
|
|
@@ -1979,6 +2079,7 @@ async function parseDecision(raw, fileName, repoRelDir, repoBaseUrl, branch, ref
|
|
|
1979
2079
|
bodyLinks: extractBodyLinks(content),
|
|
1980
2080
|
bodyHtml,
|
|
1981
2081
|
bodyRef: "",
|
|
2082
|
+
bodyId: "",
|
|
1982
2083
|
searchText: toSearchText(content),
|
|
1983
2084
|
rstatus: data.status != null ? String(data.status) : "",
|
|
1984
2085
|
isLatest: isLatestPath(filePath),
|
package/dist/render.js
CHANGED
|
@@ -100,7 +100,7 @@ function renderSite(site, assets) {
|
|
|
100
100
|
const githubBtn = site.repoBaseUrl
|
|
101
101
|
? `<a class="gh-btn" href="${escapeHtml(site.repoBaseUrl)}" target="_blank" rel="noopener" title="コンテキストリポジトリをGitHubで開く">${GITHUB_ICON}</a>`
|
|
102
102
|
: "";
|
|
103
|
-
const askBtn = `<a class="ask-ai-btn" href="${escapeHtml(askUrl)}" data-ai-prompt="${escapeHtml(ASK_PROMPT)}" target="_blank" rel="noopener" title="Claude Code Web
|
|
103
|
+
const askBtn = `<a class="ask-ai-btn" href="${escapeHtml(askUrl)}" data-ai-prompt="${escapeHtml(ASK_PROMPT)}" target="_blank" rel="noopener" title="Claude Code Web でこのプロジェクトのリポジトリと、登録済みの開発リポジトリを開いて、質問・調査できます">💬 プロジェクトについてAIに聞く</a>`;
|
|
104
104
|
// CSPでインラインが拒否される環境向けの外部参照形。省略時は従来どおり埋め込む
|
|
105
105
|
const styleTag = assets
|
|
106
106
|
? `<link rel="stylesheet" href="${escapeHtml(assets.css)}" />`
|
|
@@ -3556,7 +3556,7 @@ function buildTutorialData(site, repoSlug) {
|
|
|
3556
3556
|
},
|
|
3557
3557
|
{
|
|
3558
3558
|
title: "ドメインを可視化する",
|
|
3559
|
-
desc: "
|
|
3559
|
+
desc: "プロジェクトの概念と関係を図にする",
|
|
3560
3560
|
prompt: "このプロジェクトの主要なドメイン概念(用語集)と、それらの関係性を、HTML アーティファクトで見やすく可視化してください。カードやボックスと線でノードと関係を表す軽量な作りにし、描画に時間のかかる精密な SVG 座標計算は避けてください。",
|
|
3561
3561
|
},
|
|
3562
3562
|
{
|
|
@@ -3616,7 +3616,7 @@ function buildTutorialData(site, repoSlug) {
|
|
|
3616
3616
|
if (usesSlack) {
|
|
3617
3617
|
specs.push({
|
|
3618
3618
|
title: "Slackに完了を知らせよう",
|
|
3619
|
-
desc: "
|
|
3619
|
+
desc: "チュートリアルの完了をプロジェクトのチャンネルに投稿する",
|
|
3620
3620
|
prompt: TUT_SLACK_PROMPT,
|
|
3621
3621
|
});
|
|
3622
3622
|
}
|
|
@@ -3681,13 +3681,13 @@ const TUT_DESIGN_PROMPT = [
|
|
|
3681
3681
|
"読むだけの練習です。インベントリはFigmaからの同期で上書きされる生成物なので、手で編集しないでください。",
|
|
3682
3682
|
].join("\n");
|
|
3683
3683
|
const TUT_SLACK_PROMPT = [
|
|
3684
|
-
"Cortex
|
|
3684
|
+
"Cortex のチュートリアルを完了したことを、このプロジェクトの Slack チャンネルに投稿してください。最後のステップです。",
|
|
3685
3685
|
"",
|
|
3686
3686
|
"1. チャット/channels.json を読んで投稿先のチャンネルを選ぶ(複数あれば description を見て判断し、迷ったらわたしに聞く)",
|
|
3687
3687
|
"2. 次の雛形で本文を作る({名前} は Cortex/Members/records/ にあるわたしのレコードの表記に合わせる)",
|
|
3688
3688
|
"",
|
|
3689
3689
|
"🎉 {名前} さんが Cortex のチュートリアルを完了しました",
|
|
3690
|
-
"できるようになったこと: Gold
|
|
3690
|
+
"できるようになったこと: Gold層からプロジェクトを把握する・自分の情報を更新する・Backlogに記票する・資料を投入する",
|
|
3691
3691
|
"使い方の質問はこのチャンネルへどうぞ",
|
|
3692
3692
|
"",
|
|
3693
3693
|
"3. 投稿先と本文をプレビューして承認を得てから投稿し、メッセージのリンクを報告する",
|
|
@@ -4183,6 +4183,21 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
|
|
|
4183
4183
|
padding: 10px 12px; font-size: 13px; line-height: 1.8; color: var(--muted);
|
|
4184
4184
|
}
|
|
4185
4185
|
.draft-note > .deleted-note { flex: 1 0 100%; margin-bottom: 0; }
|
|
4186
|
+
/* 「画面が古くなっています」の帯(#382)。本文の代わりに出るので、本文と同じ幅で
|
|
4187
|
+
目に入る濃さにする。帯は症状・行は一手——文は1つ、押すものは1つだけ */
|
|
4188
|
+
.stale-note {
|
|
4189
|
+
display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 4px 0 16px;
|
|
4190
|
+
background: #fffbeb; border: 1px solid #fcd34d; border-radius: 8px;
|
|
4191
|
+
padding: 10px 12px; font-size: 13.5px; line-height: 1.8; color: #92400e;
|
|
4192
|
+
}
|
|
4193
|
+
.draft-note > .stale-note { flex: 1 0 100%; margin-bottom: 0; }
|
|
4194
|
+
.stale-reload-btn {
|
|
4195
|
+
flex: 0 0 auto; cursor: pointer; white-space: nowrap;
|
|
4196
|
+
padding: 6px 14px; border-radius: 999px;
|
|
4197
|
+
border: 1px solid #b45309; background: #fff; color: #92400e;
|
|
4198
|
+
font-size: 13px; font-weight: 600;
|
|
4199
|
+
}
|
|
4200
|
+
.stale-reload-btn:hover { background: #fef3c7; }
|
|
4186
4201
|
/* 消したものは、消えたと分かる形で残す(一覧・詳細の両方) */
|
|
4187
4202
|
.is-deleted { text-decoration: line-through; color: var(--muted); }
|
|
4188
4203
|
/* 消したレコードでは、直す口(AIで編集・GitHubで編集)を押させない */
|
|
@@ -4243,11 +4258,6 @@ button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
|
|
|
4243
4258
|
background: #ecfdf5; border: 1px solid #a7f3d0; border-radius: 8px;
|
|
4244
4259
|
padding: 10px 14px; margin: 12px 0 4px; font-size: 13.5px; color: #047857; line-height: 1.7;
|
|
4245
4260
|
}
|
|
4246
|
-
.adoption-note {
|
|
4247
|
-
display: flex; gap: 8px; align-items: flex-start;
|
|
4248
|
-
background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px;
|
|
4249
|
-
padding: 10px 14px; margin: 0 0 14px; font-size: 13.5px; color: #1e40af; line-height: 1.7;
|
|
4250
|
-
}
|
|
4251
4261
|
.chip.adoption-existing, .chip.adoption-migration { background: #eff6ff; color: #1e40af; border-color: #bfdbfe; }
|
|
4252
4262
|
.chip.adoption-new { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
|
|
4253
4263
|
.chip {
|
|
@@ -5291,7 +5301,7 @@ ${calendarAddUrl.toString()}
|
|
|
5291
5301
|
return '<a class="wikilink" data-go="' + attr + '">' + disp + "</a>";
|
|
5292
5302
|
});
|
|
5293
5303
|
}
|
|
5294
|
-
// 本文は bodies
|
|
5304
|
+
// 本文は bodies/ 以下へ外出しされ詳細表示時に fetch されるため、linkify は loadBody 側で適用する
|
|
5295
5305
|
// (ここで inline bodyHtml に適用すると検索テキストにHTMLが混ざるため適用しない)。
|
|
5296
5306
|
|
|
5297
5307
|
// Gold層ディレクトリ名(英語)→ タブ・フォルダの表示名(日本語)の辞書。
|
|
@@ -5749,7 +5759,7 @@ ${calendarAddUrl.toString()}
|
|
|
5749
5759
|
// Home(案件の入口)のAI編集。Gold層の4区画と違い規約READMEが無いので、Home自身を読ませて直させる。
|
|
5750
5760
|
// 「このプロジェクトが目指すもの」はAIが判断の方向を決める材料なので、未記入なら埋めることを促す。
|
|
5751
5761
|
var HOME_EDIT_PROMPT = [
|
|
5752
|
-
"Cortex/Home.md
|
|
5762
|
+
"Cortex/Home.md を読んでから、このプロジェクトの入口ページを修正してください。",
|
|
5753
5763
|
"何を直すかは、まずわたしに質問してください。「この案件について」「ゴール」が未記入なら、そこを埋めるところから始めてください。",
|
|
5754
5764
|
"frontmatter(識別カード・engine 設定)は触らないでください。日付・進捗・実績値は書かないでください(変わるものの正本は課題管理ツール等の外部にあります)。",
|
|
5755
5765
|
"このサイトの使い方の説明は書かないでください(説明は画面の側が持っています)。",
|
|
@@ -5764,10 +5774,10 @@ ${calendarAddUrl.toString()}
|
|
|
5764
5774
|
* 「何をする案件か・誰のためか・どうなったら成功か」を必ず聞かせる。
|
|
5765
5775
|
*/
|
|
5766
5776
|
var HOME_WRITE_PROMPT = [
|
|
5767
|
-
"Cortex/Home.md
|
|
5768
|
-
"まず、わたしに質問して内容を聞き出してください。少なくとも次の3つを確かめてください:
|
|
5777
|
+
"Cortex/Home.md を読んでから、このプロジェクトの「この案件について」「ゴール」の2つの節を埋めてください。",
|
|
5778
|
+
"まず、わたしに質問して内容を聞き出してください。少なくとも次の3つを確かめてください: 何をするプロジェクトか/誰のためのプロジェクトか(顧客・利用者は誰か)/どうなったら成功と言えるか。",
|
|
5769
5779
|
"聞き出した内容をもとに、2つの節を具体的な散文で書いてください。雛形の {{…}} は残さず、実際の内容に置き換えてください。",
|
|
5770
|
-
"この2つの節は、毎晩のAI
|
|
5780
|
+
"この2つの節は、毎晩のAI生成(決定事項・用語集・メンバー・ルールの抽出)がプロジェクトを理解するために読む場所です。抽象的な言葉ではなく、このプロジェクトに固有の具体で書いてください。",
|
|
5771
5781
|
"このサイトの使い方の説明は書かないでください(説明は画面の側が持っています)。",
|
|
5772
5782
|
"frontmatter(識別カード・engine 設定)は触らないでください。日付・進捗・実績値は書かないでください(変わるものの正本は課題管理ツール等の外部にあります)。",
|
|
5773
5783
|
"書き込む前に、書いた内容を提示して承認を得てください。",
|
|
@@ -5829,7 +5839,7 @@ ${calendarAddUrl.toString()}
|
|
|
5829
5839
|
noun: "ルール",
|
|
5830
5840
|
dir: "Cortex/Rules",
|
|
5831
5841
|
add: [
|
|
5832
|
-
"Cortex/Rules/README.md と Cortex/Rules/template.md
|
|
5842
|
+
"Cortex/Rules/README.md と Cortex/Rules/template.md を読んで登録の規約を把握してから、ルール(プロジェクトで継続的に守る制約)を1件登録してください。",
|
|
5833
5843
|
"何を登録するかは、まずわたしに質問してください。",
|
|
5834
5844
|
"書き込む前に、作成するファイルの内容を提示して承認を得てください。",
|
|
5835
5845
|
].join("\\n"),
|
|
@@ -5988,16 +5998,88 @@ ${calendarAddUrl.toString()}
|
|
|
5988
5998
|
});
|
|
5989
5999
|
return box;
|
|
5990
6000
|
}
|
|
5991
|
-
|
|
6001
|
+
/**
|
|
6002
|
+
* この画面が古くなったか(開いたあとに並びが変わったか)。
|
|
6003
|
+
*
|
|
6004
|
+
* 立つのは2つ: 本文の印が食い違った・削除が成功した。立ったあとの詳細表示は
|
|
6005
|
+
* 本文を取りに行かず帯を出す(cortex-engine #382)。
|
|
6006
|
+
*/
|
|
6007
|
+
var pageStale = false;
|
|
6008
|
+
/**
|
|
6009
|
+
* 本文の先頭に埋めた印(build.ts の bodyStampComment と対)。
|
|
6010
|
+
* **両側を同じ形にしておくこと**——片方だけ変えると、印が常に食い違って
|
|
6011
|
+
* 全レコードの本文が出なくなる(帯だけの画面になる)。
|
|
6012
|
+
*/
|
|
6013
|
+
// ⚠ 埋め込みJSはテンプレート文字列の中なので、正規表現の逆スラッシュは2本で書くこと
|
|
6014
|
+
// (1本だと素の s になり、**印が常に食い違って全レコードの本文が消える**)
|
|
6015
|
+
var BODY_STAMP_RE = /^\\s*<!--\\s*cortex-body id="([^"]*)"\\s*-->/;
|
|
6016
|
+
/**
|
|
6017
|
+
* 「画面が古くなっています」の帯。**帯は症状・行は一手・繰り返さない。**
|
|
6018
|
+
*
|
|
6019
|
+
* 🔴 **自動で読み込み直さない。** 押した人の書きかけ・開いている場所を勝手に捨てない。
|
|
6020
|
+
* 押す口は1つだけ置いて、押すかどうかは読み手が決める。
|
|
6021
|
+
*/
|
|
6022
|
+
function staleReloadNote() {
|
|
6023
|
+
var box = el("div", { class: "stale-note" }, [
|
|
6024
|
+
el("span", { text: "画面が古くなっています。再読み込みしてください。" }),
|
|
6025
|
+
]);
|
|
6026
|
+
var btn = el("button", { class: "stale-reload-btn", type: "button", text: "再読み込み" });
|
|
6027
|
+
btn.addEventListener("click", function () { location.reload(); });
|
|
6028
|
+
box.appendChild(btn);
|
|
6029
|
+
return box;
|
|
6030
|
+
}
|
|
6031
|
+
/**
|
|
6032
|
+
* 本文の代わりに帯を出す(本文は出さない——別レコードの本文を読ませないため)。
|
|
6033
|
+
*
|
|
6034
|
+
* 🔴 **帯を出す経路の after(done)はここ1か所に集める。** Home は本文が決まるまで
|
|
6035
|
+
* 枠を隠している(renderHome)ので、呼び忘れた経路は**帯ごと消えて真っ白になる**。
|
|
6036
|
+
* 3経路(印の食い違い・404/403・削除後)で書き分けると、1つだけ落ちても気づけない。
|
|
6037
|
+
*/
|
|
6038
|
+
function showStaleBody(mount, after) {
|
|
6039
|
+
pageStale = true;
|
|
6040
|
+
mount.innerHTML = "";
|
|
6041
|
+
mount.appendChild(staleReloadNote());
|
|
6042
|
+
if (typeof after === "function") after(mount);
|
|
6043
|
+
}
|
|
6044
|
+
// 本文HTMLは bodies/<種別>/<id>.html に外出しされている。詳細表示時にfetchして埋める
|
|
6045
|
+
// (キャッシュ・失敗時フォールバック付き)
|
|
5992
6046
|
var bodyCache = {};
|
|
5993
6047
|
function loadBody(rec, mount, after) {
|
|
5994
6048
|
function done() { if (typeof after === "function") after(mount); }
|
|
6049
|
+
// 並びが変わったと分かっている画面では、本文を取りに行かない(取れても別レコードのもの)
|
|
6050
|
+
if (pageStale && rec && rec.bodyRef) { showStaleBody(mount, after); return; }
|
|
5995
6051
|
if (!rec || !rec.bodyRef) { mount.innerHTML = linkifyWiki((rec && rec.bodyHtml) || ""); done(); return; }
|
|
5996
6052
|
if (bodyCache[rec.bodyRef] != null) { mount.innerHTML = bodyCache[rec.bodyRef]; done(); return; }
|
|
5997
6053
|
mount.innerHTML = '<div class="body-loading">読み込み中…</div>';
|
|
5998
|
-
fetch(rec.bodyRef).then(function (res) {
|
|
5999
|
-
|
|
6000
|
-
|
|
6054
|
+
fetch(rec.bodyRef).then(function (res) {
|
|
6055
|
+
// 🔴 **本文が無いのは「配信されていない」ではなく「この画面が古い」。** 配布は
|
|
6056
|
+
// aws s3 sync --delete なので、開いたままの画面が指す本文は建て直しで消える。
|
|
6057
|
+
//
|
|
6058
|
+
// 🔴 **403 も同じ扱い(実装レビューの指摘)。** 本番の配信は CloudFront の OAC で、
|
|
6059
|
+
// 一覧の権限を渡していないので**無いオブジェクトは 404 ではなく 403 で返る**
|
|
6060
|
+
// (403 を握りつぶすカスタムエラー応答も置いていない)。404 だけを見ていると、
|
|
6061
|
+
// この帯は**本番でだけ出ない**。403 は通行証が切れたときにも返るが、
|
|
6062
|
+
// そのときも一手は同じ(再読み込みで入り口へ戻って取り直す)
|
|
6063
|
+
if (res.status === 404 || res.status === 403) { var e = new Error("stale"); e.stale = true; throw e; }
|
|
6064
|
+
if (!res.ok) throw new Error("http " + res.status);
|
|
6065
|
+
return res.text();
|
|
6066
|
+
})
|
|
6067
|
+
.then(function (html) {
|
|
6068
|
+
// 🔴 **印が一致しない本文は出さない。** 別のレコードの本文を、見出しはそのままに
|
|
6069
|
+
// 出してしまうのが cortex-engine #382 の症状——読み手は「その決定はそう決まった」と
|
|
6070
|
+
// 受け取るし、画面は正常に見えるので**見比べない限り気づけない**。
|
|
6071
|
+
// 読めないのは気づけるが、違うものが読めるのは気づけない。だから出さない側に倒す。
|
|
6072
|
+
var m = BODY_STAMP_RE.exec(html);
|
|
6073
|
+
if (rec.bodyId && (!m || m[1] !== rec.bodyId)) {
|
|
6074
|
+
// キャッシュにも入れない(正しい本文で開き直せるように)
|
|
6075
|
+
showStaleBody(mount, after);
|
|
6076
|
+
return;
|
|
6077
|
+
}
|
|
6078
|
+
var lk = linkifyWiki(m ? html.slice(m[0].length) : html);
|
|
6079
|
+
bodyCache[rec.bodyRef] = lk; mount.innerHTML = lk; done();
|
|
6080
|
+
})
|
|
6081
|
+
.catch(function (err) {
|
|
6082
|
+
if (err && err.stale) { showStaleBody(mount, after); return; }
|
|
6001
6083
|
// 🔴 文字列連結でHTMLを組み立てない(editUrl は frontmatter 由来の外部文字列)。
|
|
6002
6084
|
// el() は class/text/html 以外のキーをそのまま setAttribute に渡すので、
|
|
6003
6085
|
// href に javascript: を置けてしまう。**isHttp() が唯一の防御**(保険ではない)
|
|
@@ -6114,22 +6196,22 @@ ${calendarAddUrl.toString()}
|
|
|
6114
6196
|
var base = SITE.repoBaseUrl;
|
|
6115
6197
|
var INFO = {
|
|
6116
6198
|
"decisions": {
|
|
6117
|
-
text: "
|
|
6199
|
+
text: "このプロジェクトで決まったことの記録です。何を・いつ・誰が決めたかと、その根拠になった会議や課題を辿れます。",
|
|
6118
6200
|
btnLabel: "実行履歴を確認 →",
|
|
6119
6201
|
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
6120
6202
|
},
|
|
6121
6203
|
"dir:Glossary": {
|
|
6122
|
-
text: "
|
|
6204
|
+
text: "このプロジェクトで使う言葉の意味をそろえる辞書です。人によって呼び方が分かれる言葉や、このプロジェクトだけの用語を載せます。",
|
|
6123
6205
|
btnLabel: "実行履歴を確認 →",
|
|
6124
6206
|
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
6125
6207
|
},
|
|
6126
6208
|
"dir:Members": {
|
|
6127
|
-
text: "
|
|
6209
|
+
text: "このプロジェクトに関わる人の名簿です。役割と所属を持ち、資料ごとに違う表記(漢字・ローマ字など)を同じ人として結び付けます。",
|
|
6128
6210
|
btnLabel: "実行履歴を確認 →",
|
|
6129
6211
|
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
6130
6212
|
},
|
|
6131
6213
|
"dir:Rules": {
|
|
6132
|
-
text: "
|
|
6214
|
+
text: "このプロジェクトで継続して守る決まりごとです。一度きりの決定ではなく、以後ずっと適用される制約や作法を集めます。",
|
|
6133
6215
|
btnLabel: "実行履歴を確認 →",
|
|
6134
6216
|
btnUrl: base ? base + "/actions/workflows/update-gold.yml" : null,
|
|
6135
6217
|
},
|
|
@@ -6166,20 +6248,15 @@ ${calendarAddUrl.toString()}
|
|
|
6166
6248
|
renderGenericList();
|
|
6167
6249
|
}
|
|
6168
6250
|
|
|
6169
|
-
// 導入経緯(overview.adoption)の表示情報。
|
|
6251
|
+
// 導入経緯(overview.adoption)の表示情報。Home見出し横のチップに出すラベルだけを返す。
|
|
6170
6252
|
function adoptionInfo(v) {
|
|
6171
|
-
//
|
|
6172
|
-
|
|
6173
|
-
if (v === "
|
|
6174
|
-
if (v === "
|
|
6175
|
-
if (v === "new") return { label: "新規導入", note: "" };
|
|
6253
|
+
// 3種とも同じ形(ラベルのみ)。導入前のDecisionの薄さの注記は出さない
|
|
6254
|
+
if (v === "existing") return { label: "既存プロジェクトに導入" };
|
|
6255
|
+
if (v === "migration") return { label: "Cortex移行" };
|
|
6256
|
+
if (v === "new") return { label: "新規導入" };
|
|
6176
6257
|
return null;
|
|
6177
6258
|
}
|
|
6178
6259
|
|
|
6179
|
-
function homeAdoption() {
|
|
6180
|
-
return SITE.home && SITE.home.fm ? adoptionInfo(SITE.home.fm.adoption) : null;
|
|
6181
|
-
}
|
|
6182
|
-
|
|
6183
6260
|
// ---------- はじめに(チュートリアル・Homeタブ下部に統合) ----------
|
|
6184
6261
|
// localStorageキーは案件別(repoSlug)。同一ブラウザで複数案件のビューアを見ても混ざらない
|
|
6185
6262
|
function tutStoreKey(n) { return "cortex-tutorial-done:" + ((TUTORIAL && TUTORIAL.slug) || "") + ":" + n; }
|
|
@@ -6477,7 +6554,7 @@ ${calendarAddUrl.toString()}
|
|
|
6477
6554
|
circle.setAttribute("aria-expanded", siteIconMenuOpen ? "true" : "false");
|
|
6478
6555
|
// 未設定は「+」が見えているので、説明は「変更」ではなく置ける話にしない——
|
|
6479
6556
|
// 読み上げと tooltip は、いまの状態でできることをそのまま言う
|
|
6480
|
-
var label = cur ? "
|
|
6557
|
+
var label = cur ? "プロジェクトアイコンを変更/外す" : "プロジェクトアイコンを設定";
|
|
6481
6558
|
circle.setAttribute("title", label);
|
|
6482
6559
|
circle.setAttribute("aria-label", label);
|
|
6483
6560
|
} else {
|
|
@@ -7046,10 +7123,10 @@ ${calendarAddUrl.toString()}
|
|
|
7046
7123
|
function homeUnfilledCard() {
|
|
7047
7124
|
var card = el("div", { class: "home-unfilled" }, [
|
|
7048
7125
|
el("div", { class: "home-unfilled-body" }, [
|
|
7049
|
-
el("div", { class: "home-unfilled-title", text: "
|
|
7126
|
+
el("div", { class: "home-unfilled-title", text: "このプロジェクトの概要とゴールが未記入です。" }),
|
|
7050
7127
|
el("div", {
|
|
7051
7128
|
class: "home-unfilled-text",
|
|
7052
|
-
text: "書かれていないと、毎晩のAI
|
|
7129
|
+
text: "書かれていないと、毎晩のAI生成(決定事項・用語・ルール・名簿)がこのプロジェクトを誤解したまま抽出を続けます。",
|
|
7053
7130
|
}),
|
|
7054
7131
|
]),
|
|
7055
7132
|
]);
|
|
@@ -7721,7 +7798,7 @@ ${calendarAddUrl.toString()}
|
|
|
7721
7798
|
var wrap = el("div", { class: "sec-how" });
|
|
7722
7799
|
wrap.appendChild(el("p", { text: "取り込まれる条件" }));
|
|
7723
7800
|
var ul = el("ul", { class: "sec-how-list" });
|
|
7724
|
-
ul.appendChild(el("li", { text: "
|
|
7801
|
+
ul.appendChild(el("li", { text: "予定がこのプロジェクトのカレンダーにある" }));
|
|
7725
7802
|
ul.appendChild(el("li", { text: "Gemini のメモが ON" }));
|
|
7726
7803
|
wrap.appendChild(ul);
|
|
7727
7804
|
wrap.appendChild(el("p", { text: "この 2 つが揃った会議だけが取り込まれます。" }));
|
|
@@ -7739,7 +7816,7 @@ ${calendarAddUrl.toString()}
|
|
|
7739
7816
|
*/
|
|
7740
7817
|
wrap.appendChild(el("p", {
|
|
7741
7818
|
class: "mt-scope",
|
|
7742
|
-
text: "
|
|
7819
|
+
text: "プロジェクトカレンダーの予定に添付されたメモだけを開きます。それ以外は中身を開きません。",
|
|
7743
7820
|
}));
|
|
7744
7821
|
// ---- 2番目: 案件カレンダーの行(#428) ----
|
|
7745
7822
|
//
|
|
@@ -7763,7 +7840,7 @@ ${calendarAddUrl.toString()}
|
|
|
7763
7840
|
// **待ちの1行は続けない**(作る場所が分からない人に、作った後の話をしない)
|
|
7764
7841
|
wrap.appendChild(el("p", {
|
|
7765
7842
|
class: "mt-cal-off",
|
|
7766
|
-
text: "
|
|
7843
|
+
text: "この画面からはプロジェクトカレンダーを確かめられません。カレンダーの場所は cortex の担当者にご確認ください。",
|
|
7767
7844
|
}));
|
|
7768
7845
|
}
|
|
7769
7846
|
return wrap;
|
|
@@ -7830,7 +7907,7 @@ ${calendarAddUrl.toString()}
|
|
|
7830
7907
|
// まだ観測が届いていない。**帯もボタンも出さない**——ここで「まだありません」+「作る」を
|
|
7831
7908
|
// 出すと、既にカレンダーがある案件で速い人が押して 2 本目を作る
|
|
7832
7909
|
if (cal.phase === "unknown") {
|
|
7833
|
-
box.appendChild(el("p", { class: "mt-cal-note", text: "
|
|
7910
|
+
box.appendChild(el("p", { class: "mt-cal-note", text: "プロジェクトカレンダーの状況を確認中です。" }));
|
|
7834
7911
|
return;
|
|
7835
7912
|
}
|
|
7836
7913
|
|
|
@@ -7839,7 +7916,7 @@ ${calendarAddUrl.toString()}
|
|
|
7839
7916
|
if (cal.phase === "unclear") {
|
|
7840
7917
|
box.appendChild(el("p", {
|
|
7841
7918
|
class: "mt-cal-note",
|
|
7842
|
-
text: "
|
|
7919
|
+
text: "プロジェクトカレンダーの状況を確認しています。しばらく待っても変わらなければ、cortex の担当者に連絡してください。",
|
|
7843
7920
|
}));
|
|
7844
7921
|
return;
|
|
7845
7922
|
}
|
|
@@ -7847,7 +7924,7 @@ ${calendarAddUrl.toString()}
|
|
|
7847
7924
|
if (cal.phase === "ready") {
|
|
7848
7925
|
// 🔴 **説明の1行は、その操作の直前に置く(#452)。** 押す前に読むものなので、
|
|
7849
7926
|
// 操作の後ろに置くと「押してから理由を知る」順になる
|
|
7850
|
-
box.appendChild(el("p", { class: "mt-cal-note", text: "
|
|
7927
|
+
box.appendChild(el("p", { class: "mt-cal-note", text: "予定を作るときに、カレンダーの欄でこのプロジェクトを選びます" }));
|
|
7851
7928
|
// 手順の画像。**上の1文だけで手順が完結する**(読み上げ・画像が出ない環境)ので、
|
|
7852
7929
|
// alt にも同じ内容を1文で書く。寸法は属性で明示——書かないと、モーダルを開いた
|
|
7853
7930
|
// 瞬間に画像の高さのぶん下の操作がずれる(Google Chat の手順と同じ理由)。
|
|
@@ -7855,13 +7932,13 @@ ${calendarAddUrl.toString()}
|
|
|
7855
7932
|
// 表示幅の上限とクリックでの原寸戻しは guideShot / .mt-shot が持つ(#455)
|
|
7856
7933
|
box.appendChild(guideShot({
|
|
7857
7934
|
src: MEET_SHOT_CREATE_EVENT, width: "1360", height: "949",
|
|
7858
|
-
alt: "
|
|
7935
|
+
alt: "予定の作成ダイアログ。「カレンダー」の欄でプロジェクトのカレンダーが選ばれ、Google Meet を追加するボタンが示され、左の一覧の「他のカレンダー」に「Google カレンダーに追加」したプロジェクトカレンダーが並んでいる。",
|
|
7859
7936
|
}));
|
|
7860
7937
|
// 🔴 **全員が必ず1回押すものなので主ボタンにする**(#452 のオーナー指摘)。
|
|
7861
7938
|
// 見た目は「この案件のカレンダーを作る」= .ds-toggle-btn を**そのまま借りる**
|
|
7862
7939
|
// ——色・枠・丸みを写すと値が2枚になり、片方だけ直る。.mt-cal-primary は
|
|
7863
7940
|
// リンク固有の分(inline-block・下線を外す)だけを足す
|
|
7864
|
-
box.appendChild(el("p", { class: "mt-cal-note", text: "最初に 1
|
|
7941
|
+
box.appendChild(el("p", { class: "mt-cal-note", text: "最初に 1 回だけ。追加すると、上の画面でこのプロジェクトを選べるようになります" }));
|
|
7865
7942
|
box.appendChild(el("a", {
|
|
7866
7943
|
class: "ds-toggle-btn mt-cal-primary", href: calendarAddUrl(cal.calendarId), target: "_blank", rel: "noopener",
|
|
7867
7944
|
text: "Google カレンダーに追加 →",
|
|
@@ -7891,7 +7968,7 @@ ${calendarAddUrl.toString()}
|
|
|
7891
7968
|
// たまにしかしないので、一番下に小さく薄い文字リンクで出す
|
|
7892
7969
|
box.appendChild(el("a", {
|
|
7893
7970
|
class: "mt-cal-sub", href: calendarTemplateUrl(cal.calendarId), target: "_blank", rel: "noopener",
|
|
7894
|
-
text: "
|
|
7971
|
+
text: "このプロジェクトのミーティングを作成 ↗",
|
|
7895
7972
|
}));
|
|
7896
7973
|
return;
|
|
7897
7974
|
}
|
|
@@ -7927,8 +8004,8 @@ ${calendarAddUrl.toString()}
|
|
|
7927
8004
|
return;
|
|
7928
8005
|
}
|
|
7929
8006
|
|
|
7930
|
-
box.appendChild(el("div", { class: "mt-cal-banner", text: "
|
|
7931
|
-
box.appendChild(calendarRequestButton("
|
|
8007
|
+
box.appendChild(el("div", { class: "mt-cal-banner", text: "プロジェクトカレンダーがまだありません" }));
|
|
8008
|
+
box.appendChild(calendarRequestButton("このプロジェクトのカレンダーを作る", "create"));
|
|
7932
8009
|
}
|
|
7933
8010
|
|
|
7934
8011
|
/**
|
|
@@ -8004,8 +8081,8 @@ ${calendarAddUrl.toString()}
|
|
|
8004
8081
|
[
|
|
8005
8082
|
// #622: 共有先はポータルで承認された案件のメンバー全員。**押す人に範囲を正しく伝える**
|
|
8006
8083
|
//(前は「案件の社内メンバーに」と書いていたが、社外のメンバーにも閲覧で共有される)
|
|
8007
|
-
"cortex-notetaker が「" + (SITE.title || "
|
|
8008
|
-
+ "
|
|
8084
|
+
"cortex-notetaker が「" + (SITE.title || "このプロジェクト") + "」のカレンダーを作り、"
|
|
8085
|
+
+ "プロジェクトの社内メンバーに予定作成の権限で、社外のメンバーに閲覧の権限で共有します。",
|
|
8009
8086
|
"できるまで数分〜十数分かかります。",
|
|
8010
8087
|
],
|
|
8011
8088
|
{ okLabel: "作る", okDanger: false, returnTo: btn },
|
|
@@ -8019,14 +8096,14 @@ ${calendarAddUrl.toString()}
|
|
|
8019
8096
|
|
|
8020
8097
|
/** 断られた人に出す1行。**ボタンは出さない**(何度押しても同じ答えなので) */
|
|
8021
8098
|
function calendarOwnerOnlyNote() {
|
|
8022
|
-
return el("p", { class: "mt-cal-note", text: "
|
|
8099
|
+
return el("p", { class: "mt-cal-note", text: "プロジェクトのオーナーが作れます。" });
|
|
8023
8100
|
}
|
|
8024
8101
|
|
|
8025
8102
|
/** ポータル経由で開いていない人に出す1行。**直し方がオーナーに頼むのとは別** */
|
|
8026
8103
|
function calendarPortalNote() {
|
|
8027
8104
|
return el("p", {
|
|
8028
8105
|
class: "mt-cal-note",
|
|
8029
|
-
text: "
|
|
8106
|
+
text: "プロジェクトカレンダーを作るには、ポータルから開き直してください。",
|
|
8030
8107
|
});
|
|
8031
8108
|
}
|
|
8032
8109
|
|
|
@@ -9639,7 +9716,7 @@ ${calendarAddUrl.toString()}
|
|
|
9639
9716
|
// 変えるときは依頼キット(docs/google-chat-onboarding.md の B)とポリシー側も同時に
|
|
9640
9717
|
var chatScope = adapter === "slack"
|
|
9641
9718
|
? ""
|
|
9642
|
-
: " Cortex
|
|
9719
|
+
: " Cortex のアプリは、会話の読み取りと、通知先に設定したスペースへの確認・通知の投稿だけに使われます。読み取った会話は、本文そのものを保存するのではなく、決定事項・用語などの抽出結果としてプロジェクトの記録に反映されます。";
|
|
9643
9720
|
m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいる" + chatNoun + "は、例外なく全て毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。読み取りを止めるには「登録を解除」してください。" + chatScope }));
|
|
9644
9721
|
} else if (cap === "開発") {
|
|
9645
9722
|
m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいるリポジトリの Issues を毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。読み取りをやめるには「登録から削除」してください。" }));
|
|
@@ -9903,7 +9980,7 @@ ${calendarAddUrl.toString()}
|
|
|
9903
9980
|
var inactive = s.enabled === false && !capPending(s.kind);
|
|
9904
9981
|
if (inactive) {
|
|
9905
9982
|
// **灰色(.ds-inactive)だけにしない。** 注記が無いと、なぜ灰色なのかが読めない
|
|
9906
|
-
col.appendChild(el("span", { class: "ds-inactive-note", text: "
|
|
9983
|
+
col.appendChild(el("span", { class: "ds-inactive-note", text: "未使用(このプロジェクトでは設定されていません)" }));
|
|
9907
9984
|
}
|
|
9908
9985
|
// id は接続マップのノードclickの飛び先(アンカー)
|
|
9909
9986
|
// **id は付けない。** #101 以前はこのカードが接続マップの飛び先だったが、
|
|
@@ -11520,7 +11597,7 @@ ${calendarAddUrl.toString()}
|
|
|
11520
11597
|
if (route === null) return "設定を読み取れないため、いまは切り替えられません。";
|
|
11521
11598
|
if (route !== "fixed-ip") {
|
|
11522
11599
|
return count > 0
|
|
11523
|
-
? "Backlog の IP
|
|
11600
|
+
? "Backlog の IP 制限で同期が失敗する場合は、これを入れてください。"
|
|
11524
11601
|
: "先に接続先を登録してください。";
|
|
11525
11602
|
}
|
|
11526
11603
|
var ipLabel = ip ? "固定の送信元 IP(" + ip + ")" : "固定の送信元 IP";
|
|
@@ -11721,9 +11798,9 @@ ${calendarAddUrl.toString()}
|
|
|
11721
11798
|
* ----------------------------------------------------------------------- */
|
|
11722
11799
|
|
|
11723
11800
|
/** リンクの title。案件リポジトリに入っていない人には開けないので、押す前に分かるようにする */
|
|
11724
|
-
var RUN_LINK_TITLE = "GitHub Actions
|
|
11801
|
+
var RUN_LINK_TITLE = "GitHub Actions の実行ログを開きます(プロジェクトリポジトリへのアクセスが要ります)";
|
|
11725
11802
|
/** 一覧(開始直後・run の番号がまだ無い)の title。飛び先が違うので文言も分ける */
|
|
11726
|
-
var RUN_LIST_TITLE = "GitHub Actions
|
|
11803
|
+
var RUN_LIST_TITLE = "GitHub Actions の実行の一覧を開きます(プロジェクトリポジトリへのアクセスが要ります)";
|
|
11727
11804
|
var RUN_LIST_TEXT = "実行の一覧 ↗";
|
|
11728
11805
|
|
|
11729
11806
|
/** 「実行ログ ↗」(新しいタブ・title つき)。一覧のときは文言と title を一覧のものにする */
|
|
@@ -12573,7 +12650,7 @@ ${calendarAddUrl.toString()}
|
|
|
12573
12650
|
// 手順一式を <details> に畳むので、**いちばん要る人に対して閉じている**
|
|
12574
12651
|
hint: [
|
|
12575
12652
|
"URL を貼ると同期先が増えます。ビューアからの起票先は変わりません(スペースごとに別のAPIキーが要ります)。",
|
|
12576
|
-
"
|
|
12653
|
+
"リアルタイム同期を使うなら、追加した Backlog プロジェクトにも、このプロジェクト専用URLの登録が要ります(同じURLを使います)。",
|
|
12577
12654
|
],
|
|
12578
12655
|
build: function (url) {
|
|
12579
12656
|
return { op: "backlog-target-add", url: url, baseGeneration: targetsGenerationOf(targets) };
|
|
@@ -12647,7 +12724,7 @@ ${calendarAddUrl.toString()}
|
|
|
12647
12724
|
// 読む人の操作対象ではない——画面から直せないものなので、次の一歩は「担当者へ連絡」。
|
|
12648
12725
|
// 「手で直してください」も出さない(PM に設定ファイルを編集させる案内になっていた)。
|
|
12649
12726
|
if (s.ingestState === "unset") {
|
|
12650
|
-
box.appendChild(el("p", { class: "sec-how", text: "
|
|
12727
|
+
box.appendChild(el("p", { class: "sec-how", text: "このプロジェクトでは議事録の自動取り込みがまだ用意されていません。cortex の担当者にご連絡ください。" }));
|
|
12651
12728
|
used = true;
|
|
12652
12729
|
} else if (s.ingestState === "broken") {
|
|
12653
12730
|
box.appendChild(el("p", { class: "sec-how", text: "議事録の取り込み設定を読み取れないため、この画面からは変更できません。cortex の担当者にご連絡ください。" }));
|
|
@@ -12656,7 +12733,7 @@ ${calendarAddUrl.toString()}
|
|
|
12656
12733
|
// 自動取り込みを止めている案件(例: Teams を使っていて手動で取り込む運用)。
|
|
12657
12734
|
// ここでトグルを出すと「押せば始まる」と読めるが、実際は設定ファイルの意思表示なので
|
|
12658
12735
|
// 画面から気軽に反転させるものではない。状態だけを伝える。
|
|
12659
|
-
box.appendChild(el("p", { class: "sec-how", text: "
|
|
12736
|
+
box.appendChild(el("p", { class: "sec-how", text: "このプロジェクトは議事録の自動取り込みを停止しています。" }));
|
|
12660
12737
|
used = true;
|
|
12661
12738
|
}
|
|
12662
12739
|
}
|
|
@@ -12719,7 +12796,7 @@ ${calendarAddUrl.toString()}
|
|
|
12719
12796
|
// 状態の出どころは資料の取り込み設定(materials-config.json)だが、会議と同じく
|
|
12720
12797
|
// 文面には出さない(2026-08-12 の文言原則)。画面から直せない状態の次の一歩は担当者への連絡。
|
|
12721
12798
|
if (s.driveState === "unset") {
|
|
12722
|
-
box.appendChild(el("p", { class: "sec-how", text: "
|
|
12799
|
+
box.appendChild(el("p", { class: "sec-how", text: "このプロジェクトでは共有資料の取り込みがまだ用意されていません。cortex の担当者にご連絡ください。" }));
|
|
12723
12800
|
used = true;
|
|
12724
12801
|
} else if (s.driveState === "broken") {
|
|
12725
12802
|
// 壊れていると書き込みも失敗する。フォームを出して押させない
|
|
@@ -13093,11 +13170,11 @@ ${calendarAddUrl.toString()}
|
|
|
13093
13170
|
// **貼ったURLを本文に出す。** 「URLを確かめてください」と言うのに出ていなかった
|
|
13094
13171
|
duplicateConfirmLines: function (value) {
|
|
13095
13172
|
return [
|
|
13096
|
-
"
|
|
13173
|
+
"このチャンネルは、別のプロジェクトでも追加されています。",
|
|
13097
13174
|
"",
|
|
13098
|
-
"
|
|
13175
|
+
"このまま追加すると、同じ会話が両方のプロジェクトの Decision・用語集に出ます。",
|
|
13099
13176
|
"",
|
|
13100
|
-
"
|
|
13177
|
+
"全体定例のように複数のプロジェクトに関係するチャンネルなら、これは正しい状態です。",
|
|
13101
13178
|
"別のチャンネルと取り違えていないか、貼ったURLを確かめてください:",
|
|
13102
13179
|
String(value || ""),
|
|
13103
13180
|
"",
|
|
@@ -14582,7 +14659,7 @@ ${calendarAddUrl.toString()}
|
|
|
14582
14659
|
var sec = el("section", { class: "about-cortex" });
|
|
14583
14660
|
sec.appendChild(el("h2", { class: "section-title", text: "この仕組みについて" }));
|
|
14584
14661
|
sec.appendChild(el("p", { class: "about-lead", text:
|
|
14585
|
-
"
|
|
14662
|
+
"議事録・課題・共有資料・デザイン・ソースコードといったプロジェクトの情報は、普段使っているツールに散らばっています。この仕組みはそれらを自動で集め、「あとで判断のよりどころになる確定した情報」だけを抽出した層を毎晩育てます。人もAIも、まずここを読めばプロジェクトを誤らずに把握できる、という状態を作るのが目的です。" }));
|
|
14586
14663
|
|
|
14587
14664
|
sec.appendChild(el("h3", { class: "about-sub", text: "情報の見方は2つあります" }));
|
|
14588
14665
|
var ways = el("ul", { class: "about-list" });
|
|
@@ -14601,9 +14678,9 @@ ${calendarAddUrl.toString()}
|
|
|
14601
14678
|
var what = el("ul", { class: "about-list" });
|
|
14602
14679
|
[
|
|
14603
14680
|
["決定事項", "何が決まったか(1決定=1ページ)。議事録や課題から毎晩自動で追加されます"],
|
|
14604
|
-
["用語集", "
|
|
14681
|
+
["用語集", "このプロジェクト固有の言葉の意味。人とAIの語彙を揃えます"],
|
|
14605
14682
|
["メンバー", "誰が関わっているか。人名の表記ゆれを解決する名簿でもあります"],
|
|
14606
|
-
["ルール", "
|
|
14683
|
+
["ルール", "このプロジェクトで継続的に守る約束事(例:「本番リリースは金曜禁止」)"],
|
|
14607
14684
|
].forEach(function (row) {
|
|
14608
14685
|
what.appendChild(el("li", {}, [
|
|
14609
14686
|
el("strong", { text: row[0] }),
|
|
@@ -14712,7 +14789,7 @@ ${calendarAddUrl.toString()}
|
|
|
14712
14789
|
var steps = [
|
|
14713
14790
|
el("ol", { class: "rt-steps" }, [
|
|
14714
14791
|
el("li", { text: "Backlog のプロジェクト設定 → インテグレーション → Webhook を開く" }),
|
|
14715
|
-
el("li", { text: "「WebHook URL
|
|
14792
|
+
el("li", { text: "「WebHook URL」に、下の(このプロジェクト専用の)URL を貼り付ける" }),
|
|
14716
14793
|
// **チェック名は受信側が処理する10種を全部並べる。** 「等」で省くと、担当者は
|
|
14717
14794
|
// 課題の3つだけにチェックして「設定した」と思い、Wikiとドキュメントの更新は
|
|
14718
14795
|
// 夜間まで届かない(画面は稼働中と出るので、欠けていることに気づけない)。
|
|
@@ -14731,7 +14808,7 @@ ${calendarAddUrl.toString()}
|
|
|
14731
14808
|
el("div", { class: "rt-url-row" }, [field, copyBtn]),
|
|
14732
14809
|
msg,
|
|
14733
14810
|
// **この画面は顧客も見る。** URLに秘密が入っていることと、その扱いを書いておく
|
|
14734
|
-
el("p", { class: "sec-note", text: "このURL
|
|
14811
|
+
el("p", { class: "sec-note", text: "このURLはこのプロジェクト専用です(他のプロジェクトには届きません)。秘密のトークンを含むので、貼り付け先は、いま設定している Backlog プロジェクトの設定欄だけにしてください。" }),
|
|
14735
14812
|
// 固定 IP 経由の案件は反映が「約1分」にならない(1分のまとめ+固定 IP の同期の起動で数分)。
|
|
14736
14813
|
// **同じ行を差し替える**——2 行に分けると「約1分」と「数分」が並んで矛盾する
|
|
14737
14814
|
el("p", { class: "sec-how", text: viaFixedIp
|
|
@@ -14743,7 +14820,7 @@ ${calendarAddUrl.toString()}
|
|
|
14743
14820
|
// 稼働中。**先頭は状態、手順は畳む。**「畳んである中に何があるか」を summary に書く
|
|
14744
14821
|
// ——ただの「詳細」だと、URLを張り替えたい人がここを開く手掛かりを持てない
|
|
14745
14822
|
var det = el("details", { class: "rt-steps-fold" }, [
|
|
14746
|
-
el("summary", { text: "
|
|
14823
|
+
el("summary", { text: "設定手順とプロジェクト専用URLを表示(URLの再確認・張り替え用)" }),
|
|
14747
14824
|
]);
|
|
14748
14825
|
for (var i = 0; i < steps.length; i++) det.appendChild(steps[i]);
|
|
14749
14826
|
// 接続先ごとに1行。**1件のときはプロジェクトキーを出さない**——どれのことかは
|
|
@@ -14875,7 +14952,7 @@ ${calendarAddUrl.toString()}
|
|
|
14875
14952
|
"backlog": {
|
|
14876
14953
|
icon: "📋",
|
|
14877
14954
|
title: "Backlog のAPIキー",
|
|
14878
|
-
how: "Backlog → 個人設定 → API
|
|
14955
|
+
how: "Backlog → 個人設定 → API で発行(同期先の Backlog プロジェクトが見えるアカウントで)",
|
|
14879
14956
|
// ⚠ **desc は足さない。** 「毎晩同期するために使います」の 1 行は「カードが長すぎる」で
|
|
14880
14957
|
// 外した経緯があり、secret-card-guidance.test.mjs が空の段落ごと見張っている。
|
|
14881
14958
|
// 用途の説明を鍵カードに足さないのは #610 でも同じ判断(2026-09-15)
|
|
@@ -14887,7 +14964,7 @@ ${calendarAddUrl.toString()}
|
|
|
14887
14964
|
// 次の人が入れ直せば済む**話で、誰か1人に依存し続けるより回復しやすい。
|
|
14888
14965
|
selfServe: {
|
|
14889
14966
|
text:
|
|
14890
|
-
"
|
|
14967
|
+
"このプロジェクトを担当しているあなたのアカウントで発行して、ここに貼ってください。" +
|
|
14891
14968
|
"管理者への依頼は要りません。",
|
|
14892
14969
|
note:
|
|
14893
14970
|
"担当が変わったら、新しい担当者が同じ手順で入れ直してください" +
|
|
@@ -15135,7 +15212,7 @@ ${calendarAddUrl.toString()}
|
|
|
15135
15212
|
// #101 でカードだけがモーダルへ移り、説明だけが画面から消えた。
|
|
15136
15213
|
// 顧客にトークンの貼り付けを求める画面で、貼った値がどう扱われるかの
|
|
15137
15214
|
// 説明が落ちるのは、いちばん落としてはいけない種類の欠落
|
|
15138
|
-
el("p", { class: "sec-note", text: "
|
|
15215
|
+
el("p", { class: "sec-note", text: "入れた値はここには表示されません(保存だけできます)。保存先はプロジェクトごとに分かれているので、他のプロジェクトには影響しません。" }),
|
|
15139
15216
|
]);
|
|
15140
15217
|
// **作った鍵カードは1枚残らずここに載せる。** 出し入れ(接続先が0件のときに畳む)は
|
|
15141
15218
|
// DOM 検索ではなくこの控えで回す——カードは能力モーダルの中身で、タイルを押すまで
|
|
@@ -15868,14 +15945,9 @@ ${calendarAddUrl.toString()}
|
|
|
15868
15945
|
|
|
15869
15946
|
// ---------- Decisions一覧(フィルタ付き) ----------
|
|
15870
15947
|
function renderDecisionList() {
|
|
15871
|
-
//
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
app.appendChild(el("div", { class: "adoption-note" }, [
|
|
15875
|
-
el("span", { text: "ℹ️" }),
|
|
15876
|
-
el("span", { text: ai.note }),
|
|
15877
|
-
]));
|
|
15878
|
-
}
|
|
15948
|
+
// 導入経緯(existing/migration)の注記はここに出さない。「AIS導入前の意思決定は
|
|
15949
|
+
// 残っていない場合があります」の帯は、オーナー決定(#626)で撤去した——帯には
|
|
15950
|
+
// その区画が何かだけを書く
|
|
15879
15951
|
var filters = el("div", { class: "filters" });
|
|
15880
15952
|
var search = el("input", { type: "search", placeholder: "キーワード検索(タイトル・要約・本文)" });
|
|
15881
15953
|
search.value = listState.q;
|
|
@@ -16260,6 +16332,30 @@ ${calendarAddUrl.toString()}
|
|
|
16260
16332
|
var meta = app.querySelector(".detail-meta");
|
|
16261
16333
|
if (meta && meta.parentNode) meta.parentNode.insertBefore(note, meta.nextSibling);
|
|
16262
16334
|
}
|
|
16335
|
+
/**
|
|
16336
|
+
* **削除を押して成功したときだけ**呼ぶ。この画面の並びが変わったことが確定するので、
|
|
16337
|
+
* 以後の詳細表示で本文を取りに行かせない(cortex-engine #382)。
|
|
16338
|
+
*
|
|
16339
|
+
* 🔴 **markRecordDeleted の中に置いてはいけない(設計レビューの指摘)。** あちらは
|
|
16340
|
+
* トゥームストーンの復元(applyDeleteTombstones → appendRecordFooter)からも呼ばれる。
|
|
16341
|
+
* 前のセッションで消したレコードを**開き直しただけ**の画面まで古い扱いになり、
|
|
16342
|
+
* そのページの本文が軒並み出なくなる。
|
|
16343
|
+
*
|
|
16344
|
+
* 自動で読み込み直さない・モーダルも出さない——押した人の開いている場所を勝手に捨てない。
|
|
16345
|
+
*/
|
|
16346
|
+
function markPageStale() {
|
|
16347
|
+
pageStale = true;
|
|
16348
|
+
if (app.querySelector(".stale-note")) return; // 帯は1本だけ(繰り返さない)
|
|
16349
|
+
var stale = staleReloadNote();
|
|
16350
|
+
// ⚠ 未確認の警告帯(.draft-note)の**中**には入れない。同じ黄色の帯が入れ子になって、
|
|
16351
|
+
// どちらが何を言っているのか読めなくなる。すぐ下に、別の帯として並べる
|
|
16352
|
+
var banner = app.querySelector(".draft-note");
|
|
16353
|
+
if (banner && banner.parentNode) { banner.parentNode.insertBefore(stale, banner.nextSibling); return; }
|
|
16354
|
+
var note = app.querySelector(".deleted-note");
|
|
16355
|
+
if (note && note.parentNode) { note.parentNode.insertBefore(stale, note.nextSibling); return; }
|
|
16356
|
+
var meta = app.querySelector(".detail-meta");
|
|
16357
|
+
if (meta && meta.parentNode) meta.parentNode.insertBefore(stale, meta.nextSibling);
|
|
16358
|
+
}
|
|
16263
16359
|
/**
|
|
16264
16360
|
* 人の確認印が付いた記録か。**3値の意味は infra の HUMAN_CONFIRMED_STATUSES と同じ**
|
|
16265
16361
|
* (active = 人が確認済みにした印/superseded = active を経た記録の差し替え後/
|
|
@@ -16293,7 +16389,7 @@ ${calendarAddUrl.toString()}
|
|
|
16293
16389
|
btn.addEventListener("click", async function () {
|
|
16294
16390
|
// **確認を await する。** 落とすと Promise が truthy で確認なしに消える
|
|
16295
16391
|
if (!(await confirmDanger(deleteConfirmLines(rec, path), { okLabel: "削除する", returnTo: btn }))) return;
|
|
16296
|
-
await sendDeleteRecord(rec, path, msg, btn, function () { markRecordDeleted(rec); });
|
|
16392
|
+
await sendDeleteRecord(rec, path, msg, btn, function () { markRecordDeleted(rec); markPageStale(); });
|
|
16297
16393
|
});
|
|
16298
16394
|
return { btn: btn, msg: msg };
|
|
16299
16395
|
}
|
|
@@ -17652,7 +17748,7 @@ ${calendarAddUrl.toString()}
|
|
|
17652
17748
|
var a = document.getElementById("home-link");
|
|
17653
17749
|
if (!a || !/^\\/p\\/[^/]+\\//.test(location.pathname)) return;
|
|
17654
17750
|
a.setAttribute("href", "/");
|
|
17655
|
-
a.setAttribute("title", "
|
|
17751
|
+
a.setAttribute("title", "プロジェクト一覧へ");
|
|
17656
17752
|
a.classList.add("site-title-portal");
|
|
17657
17753
|
})();
|
|
17658
17754
|
/**
|
package/package.json
CHANGED