eidosmd 0.1.0 → 0.3.0

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.
Files changed (107) hide show
  1. package/README.md +50 -29
  2. package/browser/dist/assets/index-Cc3cNWHY.css +1 -0
  3. package/browser/dist/assets/index-DQgCQRa5.js +46 -0
  4. package/browser/dist/favicon.svg +5 -0
  5. package/browser/dist/index.html +15 -0
  6. package/browser/dist/mark.svg +4 -0
  7. package/dist/src/cli.js +7 -0
  8. package/dist/src/commands/agents.js +1 -1
  9. package/dist/src/commands/canvas.js +77 -0
  10. package/dist/src/commands/check.js +10 -4
  11. package/dist/src/commands/configure.js +201 -0
  12. package/dist/src/commands/framework.js +37 -7
  13. package/dist/src/commands/index.js +4 -4
  14. package/dist/src/commands/init.js +5 -0
  15. package/dist/src/commands/instructions.js +1 -1
  16. package/dist/src/commands/list.js +8 -8
  17. package/dist/src/commands/migrate.js +32 -0
  18. package/dist/src/commands/new.js +3 -3
  19. package/dist/src/commands/property.js +125 -0
  20. package/dist/src/commands/seeds.js +5 -5
  21. package/dist/src/commands/setup.js +131 -0
  22. package/dist/src/commands/version.js +44 -0
  23. package/dist/src/commands/whoami.js +4 -4
  24. package/dist/src/context.js +5 -5
  25. package/dist/src/core/blueprint.js +16 -11
  26. package/dist/src/core/canvas-schema.js +148 -0
  27. package/dist/src/core/canvas.js +732 -0
  28. package/dist/src/core/check.js +221 -70
  29. package/dist/src/core/convert.js +7 -6
  30. package/dist/src/core/edits.js +1381 -0
  31. package/dist/src/core/framework-markdown.js +119 -34
  32. package/dist/src/core/framework-model.js +62 -10
  33. package/dist/src/core/framework-structured.js +222 -47
  34. package/dist/src/core/framework.js +13 -13
  35. package/dist/src/core/frontmatter.js +61 -1
  36. package/dist/src/core/git.js +84 -0
  37. package/dist/src/core/index-leaf.js +2 -2
  38. package/dist/src/core/links.js +87 -0
  39. package/dist/src/core/markdown.js +16 -9
  40. package/dist/src/core/me.js +16 -8
  41. package/dist/src/core/migrate.js +319 -0
  42. package/dist/src/core/naming.js +1 -1
  43. package/dist/src/core/regions.js +117 -0
  44. package/dist/src/core/root.js +2 -2
  45. package/dist/src/core/scaffold.js +33 -27
  46. package/dist/src/core/seed.js +187 -71
  47. package/dist/src/core/server.js +1410 -53
  48. package/dist/src/core/settings.js +232 -0
  49. package/dist/src/core/store.js +315 -0
  50. package/dist/src/core/template.js +32 -0
  51. package/dist/src/core/versions.js +84 -0
  52. package/dist/src/output.js +4 -1
  53. package/dist/src/program.js +421 -41
  54. package/instructions/authoring.md +15 -12
  55. package/instructions/configuring.md +81 -34
  56. package/instructions/init-required.md +4 -4
  57. package/instructions/overview.md +21 -9
  58. package/instructions/validating.md +9 -6
  59. package/package.json +21 -12
  60. package/standard/EIDOS.md +142 -259
  61. package/standard/seeds/README.md +12 -16
  62. package/standard/seeds/book/Framework.yaml +61 -0
  63. package/standard/seeds/book/README.md +10 -5
  64. package/standard/seeds/book/_gitignore +9 -3
  65. package/standard/seeds/book/me.md +1 -1
  66. package/standard/seeds/book/roles/README.md +3 -3
  67. package/standard/seeds/book/roles/framework-owner.md +2 -2
  68. package/standard/seeds/book/{shapes → templates}/chapter.full.md +0 -8
  69. package/standard/seeds/book/{shapes → templates}/chapter.sketch.md +0 -7
  70. package/standard/seeds/book/{shapes → templates}/frame.market.md +0 -6
  71. package/standard/seeds/book/templates/frame.premise.md +17 -0
  72. package/standard/seeds/book/{shapes → templates}/frame.reader.md +0 -6
  73. package/standard/seeds/book/{shapes → templates}/frame.voice.md +0 -7
  74. package/standard/seeds/research/Framework.yaml +61 -0
  75. package/standard/seeds/research/README.md +10 -5
  76. package/standard/seeds/research/_gitignore +9 -3
  77. package/standard/seeds/research/me.md +1 -1
  78. package/standard/seeds/research/roles/README.md +3 -3
  79. package/standard/seeds/research/roles/framework-owner.md +2 -2
  80. package/standard/seeds/research/{shapes → templates}/frame.ethics.md +0 -6
  81. package/standard/seeds/research/{shapes → templates}/frame.method.md +0 -7
  82. package/standard/seeds/research/{shapes → templates}/frame.prior-work.md +0 -6
  83. package/standard/seeds/research/{shapes → templates}/frame.question.md +0 -7
  84. package/standard/seeds/research/{shapes → templates}/investigation.full.md +0 -8
  85. package/standard/seeds/research/{shapes → templates}/investigation.note.md +0 -7
  86. package/standard/seeds/software/Framework.yaml +62 -0
  87. package/standard/seeds/software/README.md +7 -6
  88. package/standard/seeds/software/_gitignore +9 -3
  89. package/standard/seeds/software/me.md +1 -1
  90. package/standard/seeds/software/roles/README.md +3 -3
  91. package/standard/seeds/software/roles/framework-owner.md +2 -2
  92. package/standard/seeds/software/roles/project-manager.md +2 -2
  93. package/standard/seeds/software/roles/stakeholder.md +1 -1
  94. package/standard/seeds/software/{shapes → templates}/frame.architecture.md +0 -7
  95. package/standard/seeds/software/{shapes → templates}/frame.audience.md +1 -8
  96. package/standard/seeds/software/{shapes → templates}/frame.criteria.md +0 -8
  97. package/standard/seeds/software/{shapes → templates}/frame.market.md +0 -8
  98. package/standard/seeds/software/{shapes → templates}/spec.full.md +0 -8
  99. package/standard/seeds/software/{shapes → templates}/spec.micro.md +0 -9
  100. package/browser/index.html +0 -268
  101. package/dist/src/commands/convert.js +0 -30
  102. package/dist/src/core/shape.js +0 -26
  103. package/standard/seeds/book/Framework.md +0 -87
  104. package/standard/seeds/book/shapes/frame.premise.md +0 -24
  105. package/standard/seeds/research/Framework.md +0 -88
  106. package/standard/seeds/software/Framework.md +0 -88
  107. /package/standard/seeds/software/{shapes → templates}/.gitkeep +0 -0
@@ -9,7 +9,7 @@ Reviews direction and outcomes — a sponsor, a partner, a lead from another tea
9
9
  - **Vocabulary & depth:** plain, outcome-oriented language. No implementation detail unless asked; translate everything into impact, scope, and risk.
10
10
  - **Decisions:** advisory. They weigh in on direction; the Framework Owner holds the call. Don't ask them to make build decisions.
11
11
  - **Surface / hide:** surface summaries, scope, trade-offs, and risk; hide mechanism entirely.
12
- - **Focus:** Market, Criteria, Roadmap, and the shape of scope — what's in, what's out, what it costs.
12
+ - **Focus:** Market, Criteria, and the shape of scope — what's in, what's out, what it costs.
13
13
 
14
14
  ## Calibration
15
15
 
@@ -1,10 +1,3 @@
1
- <!--
2
- The Frame shape — architecture flavor. The overarching shape of the product as a built system, one
3
- per product. Suggestive, not prescriptive: keep the sections that serve the product, drop what doesn't
4
- yet apply, add what's missing. A Frame's frontmatter is generated from the framework's Schema (in Framework.md), so it is not
5
- written here. Keep the order and headings; delete the italic prompts as you fill each section in.
6
- -->
7
-
8
1
  # {{title}}
9
2
 
10
3
  _The overarching shape of the product as a built system. When the detail outgrows a single file, expand into an optional `Arch/` folder and keep this doc as the map that points into it._
@@ -1,10 +1,3 @@
1
- <!--
2
- The Frame shape — audience flavor. Who the product is for, and how each kind of person interacts with
3
- it differently. This frame and criteria are the two that decide scope. Keep it to simple blocks of
4
- prose — no tables, no headshot-and-demographics cards. Suggestive, not prescriptive. A Frame's frontmatter is
5
- generated from the framework's Schema (in Framework.md), so it is not written here. Delete the italic prompts as you fill in.
6
- -->
7
-
8
1
  # {{title}}
9
2
 
10
3
  _Who the product is for, and how each kind of person interacts with it differently. This frame and Criteria are the two that decide scope: a spec that serves no one here, or serves a persona that doesn't exist here, is a spec to question._
@@ -15,7 +8,7 @@ _Who uses this product, in a paragraph or two. The whole population it is built
15
8
 
16
9
  ## Personas
17
10
 
18
- _How each kind of person interacts differently. One short block of prose per persona. The point is the difference: what this persona comes for, how they reach for the product, what they ignore. A persona earns its place here only when it interacts in a way the others do not._
11
+ _How each kind of person interacts differently. One short block of prose per persona, never a table or a headshot-and-demographics card. The point is the difference: what this persona comes for, how they reach for the product, what they ignore. A persona earns its place here only when it interacts in a way the others do not._
19
12
 
20
13
  ### Persona name
21
14
 
@@ -1,11 +1,3 @@
1
- <!--
2
- The Frame shape — criteria flavor. The frame that decides and audits scope: what the product can
3
- afford, what it is trying to be, and by when. Paired with Audience, this is what a spec is measured
4
- against. A point-in-time snapshot — record what's true now, revise when it changes. Suggestive, not
5
- prescriptive. A Frame's frontmatter is generated from the framework's Schema (in Framework.md), so it is not written here.
6
- Delete the italic prompts as you fill each section in.
7
- -->
8
-
9
1
  # {{title}}
10
2
 
11
3
  _The frame that decides and audits scope: what the product can afford, what it is trying to be, and by when. When scope creeps, it creeps past this doc._
@@ -1,11 +1,3 @@
1
- <!--
2
- The Frame shape — market flavor. Where the product sits in its marketplace, why it is not just another
3
- one of many, and how it is intended to make money. Answers the two questions a product must survive:
4
- why would anyone pick this, and how does it earn. A point-in-time snapshot — markets move, revise when
5
- they do. Suggestive, not prescriptive. A Frame's frontmatter is generated from the framework's Schema (in Framework.md), so it
6
- is not written here. Delete the italic prompts as you fill each section in.
7
- -->
8
-
9
1
  # {{title}}
10
2
 
11
3
  _Where the product sits in its marketplace, why it is not just another one of many, and how it is intended to make money._
@@ -1,11 +1,3 @@
1
- <!--
2
- The Spec shape — the body of a spec, and the documentation of each section. A spec's
3
- frontmatter is generated from the framework's Schema (in Framework.md), so it is not written here. Keep the sections
4
- that apply and delete the rest, but leave the order and headings as they are — a reader should
5
- know what to expect from any spec in this folder. The italic prompts are guidance; delete
6
- them as you fill each section in.
7
- -->
8
-
9
1
  # {{title}}
10
2
 
11
3
  ## Intent
@@ -1,12 +1,3 @@
1
- <!--
2
- The Spec shape — micro flavor. The smallest spec worth writing: why it exists, what you're getting,
3
- and what it will not do. A starting point that grows into the full flavor (spec.full.md) as the unit
4
- firms up — add the full flavor's sections (Implementation Notes, Dependencies, Testing, Constraints &
5
- Decisions, …) when they earn their place, and set `flavor: full` (or drop `flavor`) once it has.
6
- A spec's frontmatter is generated from the framework's Schema (in Framework.md), so it is not written here. Keep the order and
7
- headings; the italic prompts are guidance — delete them as you fill each section in.
8
- -->
9
-
10
1
  # {{title}}
11
2
 
12
3
  ## Intent
@@ -1,268 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Eidos</title>
7
- <style>
8
- :root {
9
- color-scheme: light dark;
10
- --bg: #fbfaf7; --panel: #f3f1ec; --line: #ddd8cf; --text: #1f1d1a; --muted: #6b665e;
11
- --accent: #7a4a1f; --accent-soft: #f1e4d6; --error: #b23a2a; --warn: #9a6a12; --ok: #2f6b3a;
12
- --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
13
- --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
14
- }
15
- @media (prefers-color-scheme: dark) {
16
- :root { --bg: #1b1916; --panel: #232019; --line: #3a352c; --text: #ebe6dc; --muted: #a39d92; --accent: #d9a56b; --accent-soft: #3a2d1f; --error: #e2705f; --warn: #d8a437; --ok: #7bc48a; }
17
- }
18
- * { box-sizing: border-box; }
19
- body { margin: 0; font: 15px/1.5 var(--sans); color: var(--text); background: var(--bg); }
20
- a { color: var(--accent); }
21
- button, select, input, textarea { font: inherit; color: inherit; }
22
- button { background: var(--panel); border: 1px solid var(--line); border-radius: 6px; padding: 4px 10px; cursor: pointer; }
23
- button:hover { border-color: var(--accent); }
24
- button.primary { background: var(--accent); color: var(--bg); border-color: var(--accent); }
25
- input, select, textarea { background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px; width: 100%; }
26
- code, pre { font-family: var(--mono); font-size: 0.92em; }
27
- pre { background: var(--panel); padding: 10px 12px; border-radius: 6px; overflow-x: auto; }
28
- .layout { display: grid; grid-template-columns: 290px 1fr; min-height: 100vh; }
29
- aside { background: var(--panel); border-right: 1px solid var(--line); padding: 14px; overflow-y: auto; position: sticky; top: 0; height: 100vh; }
30
- main { padding: 20px 32px 60px; max-width: 980px; }
31
- .brand { display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px; }
32
- .brand strong { font-size: 18px; }
33
- .brand small { color: var(--muted); }
34
- .nav a { display: block; padding: 3px 6px; border-radius: 4px; color: inherit; text-decoration: none; }
35
- .nav a.active, .nav a:hover { background: var(--accent-soft); }
36
- .nav h3 { margin: 14px 0 4px; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); display: flex; justify-content: space-between; align-items: center; }
37
- .nav h3 button { font-size: 12px; padding: 1px 7px; }
38
- .nav h4 { margin: 8px 0 2px 6px; font-size: 12px; color: var(--muted); font-weight: 600; }
39
- .nav .bp { font-size: 14px; padding-left: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
40
- .nav .bp.flagged::after { content: " •"; color: var(--warn); }
41
- .search { margin: 6px 0 4px; }
42
- .toolbar { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; align-items: center; }
43
- .toolbar .spacer { flex: 1; }
44
- .meta { color: var(--muted); font-size: 13px; margin: 0 0 6px; }
45
- .meta code { color: var(--text); }
46
- h1.title { margin: 0 0 6px; font-size: 26px; }
47
- table.props { border-collapse: collapse; margin: 14px 0 22px; font-size: 14px; }
48
- table.props td { padding: 3px 12px 3px 0; vertical-align: top; }
49
- table.props td:first-child { color: var(--muted); font-family: var(--mono); font-size: 13px; white-space: nowrap; }
50
- .body { border-top: 1px solid var(--line); padding-top: 12px; }
51
- .body h1 { font-size: 24px; } .body h2 { font-size: 20px; margin-top: 28px; border-bottom: 1px solid var(--line); padding-bottom: 4px; } .body h3 { font-size: 16px; }
52
- .body table { border-collapse: collapse; } .body th, .body td { border: 1px solid var(--line); padding: 4px 8px; }
53
- .body em:only-child { color: var(--muted); }
54
- .findings { margin: 10px 0 16px; padding: 0; list-style: none; font-size: 14px; }
55
- .findings li { padding: 4px 8px; border-left: 3px solid var(--warn); background: var(--panel); margin-bottom: 4px; border-radius: 0 4px 4px 0; }
56
- .findings li.error { border-left-color: var(--error); }
57
- .findings code { color: var(--muted); }
58
- .badge { display: inline-block; font-size: 12px; padding: 1px 8px; border-radius: 10px; background: var(--panel); border: 1px solid var(--line); color: var(--muted); margin-left: 6px; }
59
- .badge.error { color: var(--error); border-color: var(--error); } .badge.warn { color: var(--warn); border-color: var(--warn); } .badge.ok { color: var(--ok); border-color: var(--ok); }
60
- textarea.editor { min-height: 60vh; font-family: var(--mono); font-size: 13px; line-height: 1.45; white-space: pre; }
61
- .form { display: grid; grid-template-columns: 140px 1fr; gap: 10px 14px; max-width: 640px; align-items: center; margin: 16px 0; }
62
- .form label { color: var(--muted); font-size: 14px; }
63
- .toast { position: fixed; right: 20px; bottom: 20px; background: var(--text); color: var(--bg); padding: 10px 16px; border-radius: 8px; max-width: 420px; font-size: 14px; white-space: pre-wrap; box-shadow: 0 6px 24px rgba(0,0,0,.25); }
64
- .file-group { margin: 18px 0 6px; font-family: var(--mono); font-size: 14px; }
65
- .file-group a { text-decoration: none; }
66
- .hint { color: var(--muted); font-size: 13px; }
67
- .empty { color: var(--muted); padding: 40px 0; }
68
- .fw h2 { margin-top: 26px; }
69
- .fw dl { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; }
70
- .fw dt { color: var(--muted); }
71
- @media (max-width: 760px) { .layout { grid-template-columns: 1fr; } aside { position: static; height: auto; } main { padding: 16px; } }
72
- </style>
73
- </head>
74
- <body>
75
- <div class="layout">
76
- <aside>
77
- <div class="brand"><strong id="rootName">Eidos</strong><small id="rootVersion"></small></div>
78
- <input class="search" id="search" type="search" placeholder="Find a blueprint…" autocomplete="off">
79
- <nav class="nav" id="nav"></nav>
80
- </aside>
81
- <main id="main"><div class="empty">Loading…</div></main>
82
- </div>
83
- <script>
84
- (() => {
85
- const $ = (sel, el = document) => el.querySelector(sel);
86
- const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
87
- const api = async (method, url, body) => {
88
- const res = await fetch(url, { method, headers: body ? { 'content-type': 'application/json' } : {}, body: body ? JSON.stringify(body) : undefined });
89
- const data = await res.json().catch(() => ({}));
90
- if (!res.ok) throw new Error(data.error || res.statusText);
91
- return data;
92
- };
93
- let state = { root: null, filter: '', flagged: new Set() };
94
- let toastTimer;
95
- const toast = (msg, ms = 4200) => {
96
- let el = $('.toast'); if (!el) { el = document.createElement('div'); el.className = 'toast'; document.body.appendChild(el); }
97
- el.textContent = msg; clearTimeout(toastTimer); toastTimer = setTimeout(() => el.remove(), ms);
98
- };
99
- const route = () => { const h = location.hash.replace(/^#\/?/, ''); const [view, ...rest] = h.split('/'); return { view: view || 'framework', arg: rest.join('/') }; };
100
- const go = (path) => { location.hash = '#/' + path; };
101
- const bpHref = (p) => '#/blueprint/' + p.split('/').map(encodeURIComponent).join('/');
102
-
103
- async function loadRoot() {
104
- state.root = await api('GET', '/api/root');
105
- $('#rootName').textContent = state.root.name;
106
- $('#rootVersion').textContent = `Eidos ${state.root.framework.eidos_version ?? '?'} · ${state.root.framework.file}`;
107
- document.title = `${state.root.name} · Eidos`;
108
- renderNav();
109
- }
110
-
111
- function renderNav() {
112
- const { framework, blueprints } = state.root;
113
- const q = state.filter.toLowerCase();
114
- const cur = route();
115
- const active = (h) => location.hash === h ? ' active' : '';
116
- let html = `<a href="#/framework" class="${active('#/framework') || (cur.view === 'framework' ? 'active' : '')}">Framework</a>
117
- <a href="#/check" class="${cur.view === 'check' ? 'active' : ''}">Check</a>
118
- <a href="#/standard" class="${cur.view === 'standard' ? 'active' : ''}">The standard</a>
119
- <a href="#/instructions/overview" class="${cur.view === 'instructions' ? 'active' : ''}">Agent instructions</a>`;
120
- for (const c of framework.collections) {
121
- const own = blueprints.filter((b) => b.collection === c.name && (!q || [b.title, b.id, b.summary, b.path].some((v) => (v || '').toLowerCase().includes(q))));
122
- html += `<h3><span>${esc(c.name)}</span><button data-new="${esc(c.name)}" title="New blueprint in ${esc(c.name)}">+ new</button></h3>`;
123
- const groups = [...new Set(own.map((b) => b.group).filter(Boolean))].sort();
124
- const item = (b) => `<a class="bp${state.flagged.has(b.path) ? ' flagged' : ''}${cur.view === 'blueprint' && decodeURIComponent(cur.arg) === b.path ? ' active' : ''}" href="${bpHref(b.path)}" title="${esc(b.summary || b.path)}">${esc(b.title)}</a>`;
125
- html += own.filter((b) => !b.group).map(item).join('');
126
- for (const g of groups) html += `<h4>${esc(g)}</h4>` + own.filter((b) => b.group === g).map(item).join('');
127
- if (own.length === 0) html += `<div class="hint" style="padding-left:12px">${q ? 'no match' : 'empty'}</div>`;
128
- }
129
- $('#nav').innerHTML = html;
130
- }
131
-
132
- const findingsHtml = (findings) => findings.length ? `<ul class="findings">${findings.map((f) => `<li class="${f.level}"><code>${esc(f.code)}</code> ${esc(f.message)}</li>`).join('')}</ul>` : '';
133
-
134
- async function viewBlueprint(pathArg) {
135
- const p = decodeURIComponent(pathArg);
136
- const main = $('#main');
137
- let b;
138
- try { b = await api('GET', '/api/blueprints/' + p.split('/').map(encodeURIComponent).join('/')); }
139
- catch (e) { main.innerHTML = `<div class="empty">${esc(e.message)}</div>`; return; }
140
- const errors = b.findings.filter((f) => f.level === 'error').length, warns = b.findings.length - errors;
141
- const badge = errors ? `<span class="badge error">${errors} error${errors > 1 ? 's' : ''}</span>` : '';
142
- const wbadge = warns ? `<span class="badge warn">${warns} warning${warns > 1 ? 's' : ''}</span>` : '';
143
- // a property value that is a markdown link opens the blueprint it points at
144
- const val = (x) => { const m = typeof x === 'string' && /^\s*\[([^\]]*)\]\(([^)]+)\)\s*$/.exec(x); if (!m) return `<code>${esc(x)}</code>`; const hit = state.root.blueprints.find((y) => y.path === resolveRel(p, m[2].split('#')[0])); return hit ? `<a href="${bpHref(hit.path)}">${esc(m[1])}</a>` : `<span title="not a blueprint in this root: ${esc(m[2])}">${esc(m[1])}</span> <span class="badge error">broken</span>`; };
145
- const props = Object.entries(b.properties).map(([k, v]) => `<tr><td>${esc(k)}</td><td>${Array.isArray(v) ? (v.length ? v.map(val).join(', ') : '<span class="hint">[]</span>') : (v === null || v === '' ? '<span class="hint">—</span>' : esc(v))}</td></tr>`).join('');
146
- const missing = b.shape_sections.filter((s) => !b.sections.map((x) => x.toLowerCase()).includes(s.toLowerCase()));
147
- main.innerHTML = `
148
- <div class="toolbar"><span class="meta">${esc(b.collection)}${b.group ? ' / ' + esc(b.group) : ''} · <code>${esc(b.path)}</code> · ${esc(b.flavor || '')}</span><span class="spacer"></span>
149
- <button id="edit">Edit</button></div>
150
- <h1 class="title">${esc(b.title)}${badge}${wbadge}</h1>
151
- <p class="meta">id <code>${esc(b.id || '(none)')}</code>${b.summary ? ' · ' + esc(b.summary) : ' · <span class="hint">no summary yet</span>'}</p>
152
- ${findingsHtml(b.findings)}
153
- ${missing.length ? `<p class="hint">Sections the ${esc(b.flavor)} shape declares and this blueprint lacks: ${missing.map((s) => `<code>${esc(s)}</code>`).join(', ')}</p>` : ''}
154
- <table class="props">${props}</table>
155
- <div class="body" id="body">${b.html}</div>
156
- <div id="editor" hidden><textarea class="editor" id="text"></textarea>
157
- <div class="toolbar" style="margin-top:10px"><button class="primary" id="save">Save</button><button id="cancel">Cancel</button><span class="hint">Saving runs the check on this file; the result shows above.</span></div></div>`;
158
- if (b.findings.length) state.flagged.add(p); else state.flagged.delete(p);
159
- renderNav();
160
- // relative .md links open the blueprint they point at
161
- for (const a of main.querySelectorAll('#body a[href]')) {
162
- const href = a.getAttribute('href');
163
- if (/^[a-z]+:/i.test(href) || href.startsWith('#')) continue;
164
- const target = resolveRel(p, href.split('#')[0]);
165
- const hit = state.root.blueprints.find((x) => x.path === target);
166
- if (hit) { a.href = bpHref(hit.path); } else { a.title = 'not a blueprint in this root: ' + target; a.addEventListener('click', (e) => { e.preventDefault(); toast('Not a blueprint in this root: ' + target); }); }
167
- }
168
- $('#edit').onclick = () => { $('#body').hidden = true; $('#editor').hidden = false; $('#text').value = b.raw; $('#edit').hidden = true; $('#text').focus(); };
169
- $('#cancel').onclick = () => viewBlueprint(pathArg);
170
- $('#save').onclick = async () => {
171
- try { const r = await api('PUT', '/api/blueprints/' + p.split('/').map(encodeURIComponent).join('/'), { text: $('#text').value }); toast(r.findings.length ? `Saved. ${r.findings.length} finding${r.findings.length > 1 ? 's' : ''} on this file.` : 'Saved. No findings.'); await loadRoot(); viewBlueprint(pathArg); }
172
- catch (e) { toast('Save failed: ' + e.message); }
173
- };
174
- }
175
- function resolveRel(from, href) {
176
- const parts = from.split('/').slice(0, -1);
177
- for (const seg of decodeURIComponent(href).split('/')) { if (seg === '..') parts.pop(); else if (seg !== '.' && seg !== '') parts.push(seg); }
178
- return parts.join('/');
179
- }
180
-
181
- function viewFramework() {
182
- const f = state.root.framework, a = state.root.actor;
183
- const flav = (c) => c.flavors.map((x) => `<code>${esc(x.name)}</code>${x.default ? ' (default)' : ''}${x.description ? ' — ' + esc(x.description) : ''}`).join('<br>');
184
- const canvas = (c) => !c.canvas ? '' : typeof c.canvas === 'string' ? c.canvas : `card from ## ${esc(c.canvas.section)}`;
185
- $('#main').innerHTML = `<div class="fw">
186
- <h1 class="title">${esc(state.root.name)}</h1>
187
- <p class="meta">Eidos ${esc(f.eidos_version ?? 'unversioned')} · naming <code>${esc(f.naming)}</code> · <code>${esc(f.file)}</code> · CLI ${esc(state.root.cli_version)} (standard ${esc(state.root.standard_version)})</p>
188
- ${f.problems.length ? findingsHtml(f.problems.map((m) => ({ level: 'error', code: 'framework-invalid', message: m }))) : ''}
189
- <div class="toolbar"><button id="reindex">Rebuild index</button><button id="runcheck">Run check</button><span class="hint">Both run exactly what <code>eidos index</code> and <code>eidos check</code> run.</span></div>
190
- <h2>Actor</h2><p>${a.exists && a.role ? `<strong>${esc(a.role)}</strong> · ownership: ${esc(a.ownership || '—')} · experience: ${esc(a.experience || '—')} · capacity: ${esc(a.capacity || '—')}` : `<span class="hint">unset (full facilitation) — set it with <code>eidos whoami --role &lt;name&gt;</code></span>`}</p>
191
- <p class="hint">Roles installed: ${state.root.roles.map((r) => `<code>${esc(r.name)}</code>`).join(', ') || 'none'}</p>
192
- <h2>Collections</h2>
193
- ${f.collections.map((c, i) => `<h3>${esc(c.name)}${i === 0 ? ' <span class="badge">framing</span>' : ''}${c.grouping ? ` <span class="badge">grouped by ${esc(c.grouping.label)}${c.grouping.property ? ' (' + esc(c.grouping.property) + ')' : ''}</span>` : ''}</h3>
194
- ${c.description ? `<p>${esc(c.description)}</p>` : ''}
195
- <dl><dt>flavors</dt><dd>${flav(c)}</dd>${c.canvas ? `<dt>canvas</dt><dd>${canvas(c)}</dd>` : ''}${c.grouping && c.grouping.groups ? `<dt>groups</dt><dd>${c.grouping.groups.map((g) => `<code>${esc(g.name)}</code>${g.description ? ' — ' + esc(g.description) : ''}`).join('<br>')}</dd>` : ''}</dl>`).join('')}
196
- <h2>Schema</h2>
197
- <table class="props"><tr><td>core</td><td>${f.schema.core.map((p) => `<code>${esc(p.name)}</code>`).join(' ')}</td></tr>
198
- ${f.schema.custom.map((p) => `<tr><td>${esc(p.name)}</td><td><span class="badge">${esc(p.type)}</span> <span class="badge">${Array.isArray(p.applies_to) ? esc(p.applies_to.join(', ')) : 'all'}</span> ${esc(p.meaning)}</td></tr>`).join('')}</table>
199
- ${f.top_level && f.top_level.length ? `<h2>Top-level docs</h2><ul>${f.top_level.map((d) => `<li><strong>${esc(d.title)}</strong> <code>${esc(d.path)}</code>${d.description ? ' — ' + esc(d.description) : ''}</li>`).join('')}</ul>` : ''}
200
- </div>`;
201
- $('#reindex').onclick = async () => { try { const r = await api('POST', '/api/index'); toast(r.indexes.map((i) => `${i.collection}: ${i.status} (${i.blueprints})`).join('\n') + (r.missing_summary.length ? `\n${r.missing_summary.length} blueprint(s) still need a summary` : '')); } catch (e) { toast(e.message); } };
202
- $('#runcheck').onclick = () => go('check');
203
- }
204
-
205
- async function viewCheck() {
206
- const main = $('#main'); main.innerHTML = '<div class="empty">Checking…</div>';
207
- const r = await api('GET', '/api/check');
208
- state.flagged = new Set(r.findings.map((f) => f.path).filter(Boolean));
209
- renderNav();
210
- const byFile = new Map();
211
- for (const f of r.findings) { const k = f.path || '(root)'; if (!byFile.has(k)) byFile.set(k, []); byFile.get(k).push(f); }
212
- const isBp = (k) => state.root.blueprints.some((b) => b.path === k);
213
- main.innerHTML = `<h1 class="title">Check <span class="badge ${r.errors ? 'error' : r.warnings ? 'warn' : 'ok'}">${r.errors} error${r.errors === 1 ? '' : 's'}, ${r.warnings} warning${r.warnings === 1 ? '' : 's'}</span></h1>
214
- <p class="meta">${r.blueprints} blueprints in ${r.collections} collections · Eidos ${esc(r.eidos_version ?? '?')} · ${esc(r.naming)} · the same report as <code>eidos check</code></p>
215
- ${r.findings.length === 0 ? '<p class="empty">No findings.</p>' : ''}
216
- ${[...byFile.entries()].map(([k, fs]) => `<div class="file-group">${isBp(k) ? `<a href="${bpHref(k)}">${esc(k)}</a>` : esc(k)}</div>${findingsHtml(fs)}`).join('')}`;
217
- }
218
-
219
- async function viewDoc(url, title) {
220
- const main = $('#main'); main.innerHTML = '<div class="empty">Loading…</div>';
221
- const r = await api('GET', url);
222
- main.innerHTML = `<div class="body" style="border:0">${r.html}</div>`;
223
- }
224
-
225
- function viewNew(collectionName) {
226
- const f = state.root.framework;
227
- const c = f.collections.find((x) => x.name === collectionName) || f.collections[0];
228
- const groups = (c.grouping && c.grouping.groups) ? c.grouping.groups.map((g) => g.name) : [];
229
- $('#main').innerHTML = `<h1 class="title">New blueprint</h1>
230
- <p class="hint">Runs <code>eidos new</code>: frontmatter from the Schema, body from the flavor's shape, filename in the naming convention. You fill the prose afterwards.</p>
231
- <div class="form">
232
- <label>Collection</label><select id="n-col">${f.collections.map((x) => `<option ${x.name === c.name ? 'selected' : ''}>${esc(x.name)}</option>`).join('')}</select>
233
- <label>Title</label><input id="n-title" placeholder="Session Management" autofocus>
234
- <label>Flavor</label><select id="n-flavor">${c.flavors.map((x) => `<option ${x.default ? 'selected' : ''}>${esc(x.name)}</option>`).join('')}</select>
235
- <label>Group</label><input id="n-group" list="n-groups" placeholder="${groups.length ? 'one of the declared groups, or a new one' : c.grouping ? c.grouping.label : 'optional sub-folder'}"><datalist id="n-groups">${groups.map((g) => `<option value="${esc(g)}">`).join('')}</datalist>
236
- <label>Summary</label><input id="n-summary" placeholder="One plain line: what this blueprint is.">
237
- <span></span><div class="toolbar"><button class="primary" id="n-create">Create</button><button id="n-cancel">Cancel</button></div>
238
- </div>`;
239
- $('#n-col').onchange = () => viewNew($('#n-col').value);
240
- $('#n-cancel').onclick = () => history.back();
241
- $('#n-create').onclick = async () => {
242
- try {
243
- const r = await api('POST', '/api/blueprints', { collection: $('#n-col').value, title: $('#n-title').value, flavor: $('#n-flavor').value, group: $('#n-group').value, summary: $('#n-summary').value });
244
- toast(`Created ${r.path}` + (r.notes.length ? '\n' + r.notes.join('\n') : ''), 6000);
245
- await loadRoot(); go('blueprint/' + r.path.split('/').map(encodeURIComponent).join('/'));
246
- } catch (e) { toast(e.message, 6000); }
247
- };
248
- }
249
-
250
- async function render() {
251
- const { view, arg } = route();
252
- renderNav();
253
- if (view === 'blueprint') return viewBlueprint(arg);
254
- if (view === 'check') return viewCheck();
255
- if (view === 'standard') return viewDoc('/api/standard');
256
- if (view === 'instructions') return viewDoc('/api/instructions/' + (arg || 'overview'));
257
- if (view === 'new') return viewNew(decodeURIComponent(arg));
258
- return viewFramework();
259
- }
260
-
261
- $('#search').addEventListener('input', (e) => { state.filter = e.target.value; renderNav(); });
262
- $('#nav').addEventListener('click', (e) => { const b = e.target.closest('button[data-new]'); if (b) go('new/' + encodeURIComponent(b.dataset.new)); });
263
- window.addEventListener('hashchange', render);
264
- loadRoot().then(render).catch((e) => { $('#main').innerHTML = `<div class="empty">${esc(e.message)}</div>`; });
265
- })();
266
- </script>
267
- </body>
268
- </html>
@@ -1,30 +0,0 @@
1
- // `eidos convert`: move a markdown root to Framework.yaml, the form the CLI works in.
2
- import path from 'node:path';
3
- import { loadRoot } from '../context.js';
4
- import { convertToYaml } from '../core/convert.js';
5
- import { EXIT_OK, print, printJson } from '../output.js';
6
- export function runConvert(globals, options, cwd = process.cwd()) {
7
- const context = loadRoot(globals, cwd, { allowMarkdown: true });
8
- const display = (file) => path.relative(cwd, file) || file;
9
- if (context.framework.format === 'yaml') {
10
- if (options.json) {
11
- printJson({ converted: false, framework_file: display(context.framework.file), reason: 'already YAML' });
12
- }
13
- else {
14
- print(`${display(context.framework.file)} is already the YAML form; nothing to convert`);
15
- }
16
- return EXIT_OK;
17
- }
18
- const result = convertToYaml(context.framework, options.dryRun);
19
- if (options.json) {
20
- printJson({ converted: !options.dryRun, dry_run: options.dryRun, framework_file: display(result.written), removed: result.removed.map(display), actions: result.actions.map(display) });
21
- return EXIT_OK;
22
- }
23
- print(...result.actions.map(display));
24
- print('', `${options.dryRun ? 'would convert' : 'converted'}: ${display(context.framework.file)} → ${display(result.written)}, index inside it`);
25
- print(" left behind: the markdown form's prose (section intros, comments); each collection's description travelled. Keep anything that matters in the root's README.");
26
- if (!options.dryRun) {
27
- print(' next: eidos check');
28
- }
29
- return EXIT_OK;
30
- }
@@ -1,26 +0,0 @@
1
- // A shape file: one body template in `_eidos/shapes/`, read for the sections
2
- // it declares (to validate a blueprint against) and as the template a new
3
- // blueprint's body is rendered from.
4
- import { existsSync, readFileSync } from 'node:fs';
5
- import { headings } from './markdown.js';
6
- import { shapePath } from './framework.js';
7
- export function loadShape(framework, flavor) {
8
- const file = shapePath(framework, flavor);
9
- if (!existsSync(file)) {
10
- return null;
11
- }
12
- const template = readFileSync(file, 'utf8');
13
- const all = headings(template);
14
- return {
15
- path: file,
16
- flavor,
17
- template,
18
- sections: all.filter((heading) => heading.level === 2).map((heading) => heading.text),
19
- headings: all,
20
- };
21
- }
22
- // The body a scaffolded blueprint starts from: the template with its title
23
- // filled in and any leading blank lines dropped.
24
- export function renderShape(template, title) {
25
- return template.replace(/\r\n?/g, '\n').replace(/\{\{title\}\}/g, title).replace(/^\n+/, '');
26
- }
@@ -1,87 +0,0 @@
1
- ---
2
- # The Eidos version this framework targets; migrate reads and bumps it.
3
- eidos_version: 4.5.0
4
- # How files, folders, and links are named: kebab-case | TitleCase | Title Case. Absent = kebab-case.
5
- naming: kebab-case
6
- ---
7
-
8
- # Framework
9
-
10
- The framework's index and config, in one place: the version and naming convention above, and below the
11
- Top-Level documents, the Collections (with their flavors and grouping), and the property Schema. The
12
- visible `README.md` at the root is the friendly door to it; keep it current with the
13
- `configure` skill.
14
-
15
- ## Top-Level
16
-
17
- <!-- configure: top-level index (regenerated) -->
18
- - [README](../README.md) — the root's front door: what this book is, and pointers in.
19
- <!-- One bullet per top-level document. README is the door and comes first; add your own below (an
20
- Outline, a Synopsis, the generated Blueprint Map canvas), each a link and a one-line description.
21
- The framing docs live in the Frames collection, not here. configure refreshes this list. -->
22
-
23
- ## Collections
24
-
25
- A collection is a top-level folder of repeated blueprints that share a body shape. `Frames` holds the
26
- framing docs — the most primary thing the folder says about itself — and `Chapters` the book's
27
- units. Add more with `configure`. Each lists its flavors (the default marked), how it draws on
28
- the canvas, and its grouping, and points at its generated `index.md` leaf.
29
-
30
- ### Frames
31
-
32
- The framing docs that set what every chapter is judged against — what the book argues, who it is for,
33
- how it sounds, and where it sits. This framework's framing collection; each frame follows the flavor
34
- of its kind, and one left unwritten is a gap to surface, not a failure.
35
-
36
- - **Leaf:** [Frames/index.md](../Frames/index.md)
37
- - **Flavors:**
38
- - [premise](shapes/frame.premise.md) — what the book says, and why it has to exist (default).
39
- - [reader](shapes/frame.reader.md) — who it is for, and what changes for them.
40
- - [voice](shapes/frame.voice.md) — person, tense, register, and the rules the prose keeps.
41
- - [market](shapes/frame.market.md) — shelf, comparables, and how it reaches readers.
42
- - **Canvas:** file
43
-
44
- ### Chapters
45
-
46
- The book's units, one per chapter, grouped by part.
47
-
48
- - **Leaf:** [Chapters/index.md](../Chapters/index.md)
49
- - **Flavors:**
50
- - [full](shapes/chapter.full.md) — the complete chapter shape (default).
51
- - [sketch](shapes/chapter.sketch.md) — Intent, Open Questions, What Happens, Out of Scope; grow into full.
52
- - **Canvas:** card from `## Intent`
53
- - **Parts:** _(add one bullet per part — a name and a short description — as parts accrue)_
54
-
55
- ## Schema
56
-
57
- The property contract — what a blueprint's frontmatter may carry, across every collection. Two parts: the
58
- **core** properties Eidos's own machinery uses, and the **custom** ones you (or the seed) add. Every
59
- custom property declares which collections it **applies to** — `all`, or a list — so a property never
60
- lands where it makes no sense (`part` is Chapters-only). A property's type comes from the Obsidian set
61
- (Text, List, Number, Checkbox, Date, Date & time), so frontmatter renders natively in an Obsidian
62
- vault. The `configure` skill edits this section.
63
-
64
- ### Eidos Core
65
-
66
- _Present on every blueprint. Managed by the standard (Eidos 4.5.0); `migrate` rewrites this block on a version change — don't hand-edit it. (`flavor` absent = the collection's default; `connects_to` absent = no canvas edges; a missing `summary` is flagged by the index.)_
67
-
68
- | Name | Type | Meaning |
69
- | ----------- | ---- | ---------------------------------------------------------------------------------------------- |
70
- | id | Text | Stable, unique, kebab-case identity. Assigned once, never renamed. References point at it. |
71
- | title | Text | Human-readable name. |
72
- | summary | Text | One plain line — what this blueprint is, in a sentence, distilled from Intent. Source for the collection index.md listing; absent, the index flags it. |
73
- | flavor | Text | Which body flavor this blueprint follows, from its collection's declared flavors. Absent = the collection's default flavor. |
74
- | connects_to | List | Blueprints this one connects to on the canvas, each a markdown link; drawn as a directed edge (this → target). The intentional map, distinct from depends_on. |
75
-
76
- ### Custom Properties
77
-
78
- _Yours to shape with the `configure` skill. The seed ships a few useful defaults below — keep, scope, or drop any of them; Eidos doesn't depend on them. Absence where a property applies is a soft gap the validator notes, never refuses._
79
-
80
- | Name | Type | Applies To | Meaning |
81
- | ------------- | ---- | ---------- | ------------------------------------------------------------------------------------------ |
82
- | status | Text | all | Lifecycle value: Draft / Outlined / Drafted / Revised / Final / Cut. An off-list value warns. |
83
- | date_created | Date | all | YYYY-MM-DD. The day the blueprint was first written. Set once. |
84
- | date_modified | Date | all | YYYY-MM-DD. The day the blueprint was last changed. |
85
- | tags | List | all | Free tags. |
86
- | part | Text | Chapters | The grouping, matching the blueprint's sub-folder under its collection in the naming convention. An unknown value warns, never fails. |
87
- | depends_on | List | Chapters | Chapters a reader must have read first, each a markdown link. A reading dependency, not a canvas edge. |
@@ -1,24 +0,0 @@
1
- <!--
2
- The Premise frame — what the book says and why it has to exist. Loose, point-in-time prose: record
3
- what is true now and revise when it changes. Suggestive, not prescriptive. A Frame's frontmatter is
4
- generated from the framework's Schema (in Framework.md), so it is not written here. Delete the italic
5
- prompts as you fill in.
6
- -->
7
-
8
- # Premise
9
-
10
- ## The Argument
11
-
12
- _In a few sentences: what this book claims, or what story it tells. If you can't say it without a list, it isn't a premise yet._
13
-
14
- ## Why It Has To Exist
15
-
16
- _What is wrong, missing, or misunderstood in the world without this book. The problem the reader has._
17
-
18
- ## The Shape of It
19
-
20
- _How the whole thing is built — parts, arc, the movement from the first page to the last. Not a table of contents; the logic behind one._
21
-
22
- ## What It Is Not
23
-
24
- _The adjacent book you are deliberately not writing. This is where a book's scope is held._
@@ -1,88 +0,0 @@
1
- ---
2
- # The Eidos version this framework targets; migrate reads and bumps it.
3
- eidos_version: 4.5.0
4
- # How files, folders, and links are named: kebab-case | TitleCase | Title Case. Absent = kebab-case.
5
- naming: kebab-case
6
- ---
7
-
8
- # Framework
9
-
10
- The framework's index and config, in one place: the version and naming convention above, and below the
11
- Top-Level documents, the Collections (with their flavors and grouping), and the property Schema. The
12
- visible `README.md` at the root is the friendly door to it; keep it current with the
13
- `configure` skill.
14
-
15
- ## Top-Level
16
-
17
- <!-- configure: top-level index (regenerated) -->
18
- - [README](../README.md) — the root's front door: what this programme asks, and pointers in.
19
- <!-- One bullet per top-level document. README is the door and comes first; add your own below (a
20
- Protocol, a Data Statement, the generated Blueprint Map canvas), each a link and a one-line
21
- description. The framing docs live in the Frames collection, not here. configure refreshes
22
- this list. -->
23
-
24
- ## Collections
25
-
26
- A collection is a top-level folder of repeated blueprints that share a body shape. `Frames` holds the
27
- framing docs — the most primary thing the folder says about itself — and `Investigations` the
28
- programme's units. Add more with `configure`. Each lists its flavors (the default marked), how
29
- it draws on the canvas, and its grouping, and points at its generated `index.md` leaf.
30
-
31
- ### Frames
32
-
33
- The framing docs that set what every investigation is judged against — what is being asked, what is
34
- already known, what would count as knowing, and who is affected. This framework's framing collection;
35
- each frame follows the flavor of its kind, and one left unwritten is a gap to surface, not a failure.
36
-
37
- - **Leaf:** [Frames/index.md](../Frames/index.md)
38
- - **Flavors:**
39
- - [question](shapes/frame.question.md) — what the programme asks, and why it matters (default).
40
- - [prior work](shapes/frame.prior-work.md) — what is already known, and where this sits.
41
- - [method](shapes/frame.method.md) — how anyone would know, and the standard of evidence.
42
- - [ethics](shapes/frame.ethics.md) — who is affected, what they consented to, what could go wrong.
43
- - **Canvas:** file
44
-
45
- ### Investigations
46
-
47
- The programme's units, one per line of inquiry, grouped by strand.
48
-
49
- - **Leaf:** [Investigations/index.md](../Investigations/index.md)
50
- - **Flavors:**
51
- - [full](shapes/investigation.full.md) — the complete investigation shape (default).
52
- - [note](shapes/investigation.note.md) — Intent, Open Questions, Claims, Out of Scope; grow into full.
53
- - **Canvas:** card from `## Intent`
54
- - **Strands:** _(add one bullet per strand — a name and a short description — as strands accrue)_
55
-
56
- ## Schema
57
-
58
- The property contract — what a blueprint's frontmatter may carry, across every collection. Two parts: the
59
- **core** properties Eidos's own machinery uses, and the **custom** ones you (or the seed) add. Every
60
- custom property declares which collections it **applies to** — `all`, or a list — so a property never
61
- lands where it makes no sense (`strand` is Investigations-only). A property's type comes from the
62
- Obsidian set (Text, List, Number, Checkbox, Date, Date & time), so frontmatter renders natively in an
63
- Obsidian vault. The `configure` skill edits this section.
64
-
65
- ### Eidos Core
66
-
67
- _Present on every blueprint. Managed by the standard (Eidos 4.5.0); `migrate` rewrites this block on a version change — don't hand-edit it. (`flavor` absent = the collection's default; `connects_to` absent = no canvas edges; a missing `summary` is flagged by the index.)_
68
-
69
- | Name | Type | Meaning |
70
- | ----------- | ---- | ---------------------------------------------------------------------------------------------- |
71
- | id | Text | Stable, unique, kebab-case identity. Assigned once, never renamed. References point at it. |
72
- | title | Text | Human-readable name. |
73
- | summary | Text | One plain line — what this blueprint is, in a sentence, distilled from Intent. Source for the collection index.md listing; absent, the index flags it. |
74
- | flavor | Text | Which body flavor this blueprint follows, from its collection's declared flavors. Absent = the collection's default flavor. |
75
- | connects_to | List | Blueprints this one connects to on the canvas, each a markdown link; drawn as a directed edge (this → target). The intentional map, distinct from depends_on. |
76
-
77
- ### Custom Properties
78
-
79
- _Yours to shape with the `configure` skill. The seed ships a few useful defaults below — keep, scope, or drop any of them; Eidos doesn't depend on them. Absence where a property applies is a soft gap the validator notes, never refuses._
80
-
81
- | Name | Type | Applies To | Meaning |
82
- | ------------- | ---- | -------------- | -------------------------------------------------------------------------------------- |
83
- | status | Text | all | Lifecycle value: Draft / Open / Running / Answered / Inconclusive / Abandoned. An off-list value warns. |
84
- | date_created | Date | all | YYYY-MM-DD. The day the blueprint was first written. Set once. |
85
- | date_modified | Date | all | YYYY-MM-DD. The day the blueprint was last changed. |
86
- | tags | List | all | Free tags. |
87
- | strand | Text | Investigations | The grouping — the line of inquiry this belongs to, matching the blueprint's sub-folder in the naming convention. An unknown value warns, never fails. |
88
- | depends_on | List | Investigations | Investigations, data, or approvals this one needs first, each a markdown link. A real dependency, not a canvas edge. |