enola-guides 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +19 -2
- data/examples/knowledge-base/README.md +20 -0
- data/examples/knowledge-base/enola-cluster.yaml +7 -0
- data/examples/knowledge-base/wiki/_state/enola/findings.json +10 -0
- data/examples/knowledge-base/wiki/_state/enola/receipts.json +16 -0
- data/examples/knowledge-base/wiki/shop/permanent/architecture.md +19 -0
- data/examples/knowledge-base/wiki/shop/permanent/public-surface.md +14 -0
- data/guides/09-a-knowledge-base-with-enola.md +148 -0
- data/guides/10-hooks.md +101 -0
- data/guides/11-the-skills-as-they-run.md +81 -0
- data/guides/12-the-wrapper.md +69 -0
- data/lib/enola-guides.rb +1 -1
- data/templates/hooks/intent-first-gate.sh +49 -0
- data/templates/hooks/record.sh +37 -0
- data/templates/hooks/session-refresh.sh +31 -0
- data/templates/hooks/stop-debt.sh +39 -0
- data/templates/skills/enola-citation-hygiene/SKILL.md +22 -0
- data/templates/skills/enola-deepdive/SKILL.md +21 -0
- data/templates/skills/enola-drift/SKILL.md +21 -0
- data/templates/skills/enola-ingest-check/SKILL.md +23 -0
- data/templates/skills/enola-zoom-out/SKILL.md +16 -0
- metadata +20 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a8d331c92a1d9c3343d76d079967a3d6c96ec41227c35ca99c2977c8980764b6
|
|
4
|
+
data.tar.gz: 578aebe657940e45dcab775af903b1e8f6ddd4eb47ab4880d77b81a8ae67fcf1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 57c7b7963c5d75e2750338d4f287c8be1115a808d28004f3cefae76b2823de297b3b44e94a2150638b499bb32994f4df32449494b0f2a5daa7cee5e80d3072d6
|
|
7
|
+
data.tar.gz: 7571fc1b51b77801f6d8f0c032b59f1c550a442ac1e73fb686ba5aded8efc000b2ba4c6c83aa1579c587b8b073abb095fc035c89bb88fc367d7eedaf8e4e8b65
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 (2026-08-22)
|
|
4
|
+
|
|
5
|
+
What a knowledge base gets. Four guides (a knowledge base with enola,
|
|
6
|
+
hooks, the skills as they run, the wrapper), four hook templates
|
|
7
|
+
(intent-first gate, turn record, stop-time debt, session refresh), five
|
|
8
|
+
skills as a knowledge base runs them (deepdive, ingest check, drift,
|
|
9
|
+
citation hygiene, zoom out) and a worked example knowledge base with two
|
|
10
|
+
anchored pages, receipts and a finding ledger.
|
|
11
|
+
|
|
3
12
|
## 0.1.2 (2026-08-22)
|
|
4
13
|
|
|
5
14
|
One law per file: the Rails shop, the cluster laws, the maintenance-task laws
|
data/README.md
CHANGED
|
@@ -22,7 +22,9 @@ thirteen laws in the Ruby DSL, one file each, from "jobs never invoke controller
|
|
|
22
22
|
"billing keeps to its own tables" and "a mutating action is authorized",
|
|
23
23
|
with Prism and Rubydex configured as providers beside the extractors.
|
|
24
24
|
[`examples/ember-app`](examples/ember-app): an Ember application bound to
|
|
25
|
-
the conventions recipe in one block. [`examples/
|
|
25
|
+
the conventions recipe in one block. [`examples/knowledge-base`](examples/knowledge-base/README.md):
|
|
26
|
+
two pages anchored to code, a cluster config, receipts and a finding ledger,
|
|
27
|
+
the smallest knowledge base the skills and hooks run against. [`examples/laws`](examples/laws/README.md):
|
|
26
28
|
twenty-six laws, one directory each, from foreign keys on a tenant column
|
|
27
29
|
to parts that may not cycle, each a complete declaration you copy and run.
|
|
28
30
|
|
|
@@ -43,6 +45,16 @@ to parts that may not cycle, each a complete declaration you copy and run.
|
|
|
43
45
|
census, the runtime capture.
|
|
44
46
|
8. [Beyond one repository](guides/08-beyond-one-repo.md): the cluster, the
|
|
45
47
|
seams, the laws across them, the history.
|
|
48
|
+
9. [A knowledge base with enola](guides/09-a-knowledge-base-with-enola.md):
|
|
49
|
+
what a wiki of decisions gets when its pages anchor to code and every
|
|
50
|
+
number it states was read from a receipt.
|
|
51
|
+
10. [Hooks](guides/10-hooks.md): the four agent hooks a knowledge base runs
|
|
52
|
+
(intent-first gate, turn record, stop-time debt, session refresh).
|
|
53
|
+
11. [The skills, as they run](guides/11-the-skills-as-they-run.md): deepdive
|
|
54
|
+
before shaping, claim check while ingesting, drift on a schedule,
|
|
55
|
+
citation hygiene, the blast radius.
|
|
56
|
+
12. [The wrapper](guides/12-the-wrapper.md): the thin command the skills
|
|
57
|
+
call, the named skip, the receipts and the ledger.
|
|
46
58
|
|
|
47
59
|
## Skills
|
|
48
60
|
|
|
@@ -52,7 +64,12 @@ before grepping, grade after changing), `enola-laws` (write a law as a
|
|
|
52
64
|
sentence with a reason, lint it, read what it verdicts),
|
|
53
65
|
`enola-grade-before-push` (the delta is the deliverable, a declined grade
|
|
54
66
|
is not a pass), `enola-intent-first` (read the governing decision before
|
|
55
|
-
the file moves)
|
|
67
|
+
the file moves), and the five a knowledge base runs: `enola-deepdive`,
|
|
68
|
+
`enola-ingest-check`, `enola-drift`, `enola-citation-hygiene`,
|
|
69
|
+
`enola-zoom-out`, each described as it runs in
|
|
70
|
+
[guide 11](guides/11-the-skills-as-they-run.md). Copy a directory into
|
|
71
|
+
your agent's skills folder; the hooks they pair with are under
|
|
72
|
+
[`templates/hooks/`](templates/hooks).
|
|
56
73
|
|
|
57
74
|
## Templates and recipes
|
|
58
75
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# A knowledge base with enola, minimal
|
|
2
|
+
|
|
3
|
+
The smallest knowledge base that gets what [guide 09](../../guides/09-a-knowledge-base-with-enola.md) describes:
|
|
4
|
+
two pages anchored to code with `enola_intent`, a cluster config, the
|
|
5
|
+
committed receipts, and the finding ledger. The skills under
|
|
6
|
+
[`templates/skills`](../../templates/skills) and the hooks under
|
|
7
|
+
[`templates/hooks`](../../templates/hooks) run against exactly these
|
|
8
|
+
files.
|
|
9
|
+
|
|
10
|
+
- `enola-cluster.yaml`: the members, with `knowledge` pointing at the wiki.
|
|
11
|
+
- `wiki/shop/permanent/architecture.md`: a page whose `enola_intent`
|
|
12
|
+
anchors it to `app/jobs/**` and `app/models/**`, with one receipt
|
|
13
|
+
citation.
|
|
14
|
+
- `wiki/shop/permanent/public-surface.md`: a page anchored to the
|
|
15
|
+
controllers it describes.
|
|
16
|
+
- `wiki/_state/enola/receipts.json`: what `kb enola generate` recorded.
|
|
17
|
+
- `wiki/_state/enola/findings.json`: the ledger, one judged finding.
|
|
18
|
+
|
|
19
|
+
`governing_intent app/jobs/order_mailer_job.rb` returns the first page;
|
|
20
|
+
`kb enola citations` verifies its citation against the receipts file.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repos": {
|
|
3
|
+
"shop": {
|
|
4
|
+
"fingerprint": "8d1e5a7b93c2f0e4d6a18b7c3e9f0a2d4c6b8e1f3a5c7d9e0b2f4a6c8e0d2c40a",
|
|
5
|
+
"git_commit": "3f2a9c1d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c",
|
|
6
|
+
"enola_version": "0.1.22",
|
|
7
|
+
"providers": ["prism", "rubydex"]
|
|
8
|
+
},
|
|
9
|
+
"shop-ui": {
|
|
10
|
+
"fingerprint": "2c7e9b1d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c8e",
|
|
11
|
+
"git_commit": "9b8c7d6e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c",
|
|
12
|
+
"enola_version": "0.1.22",
|
|
13
|
+
"providers": []
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shop architecture
|
|
3
|
+
enola_intent:
|
|
4
|
+
governs:
|
|
5
|
+
- app/jobs/**
|
|
6
|
+
- app/models/**
|
|
7
|
+
decisions:
|
|
8
|
+
- jobs enqueue, models decide; a job never calls back into a job
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Shop architecture
|
|
12
|
+
|
|
13
|
+
Orders are written by `OrderService` only; the two other writers the
|
|
14
|
+
graph reported in 2026-08 were folded into it. `Order` has 41 dependents
|
|
15
|
+
(receipt shop@3f2a9c1, fingerprint 8d1e…c40a).
|
|
16
|
+
|
|
17
|
+
The cycle `app/jobs -> app/models -> app/jobs` through the mailer path is
|
|
18
|
+
real and accepted in the ledger; it is cut when the mailer moves under
|
|
19
|
+
`app/services`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Public surface
|
|
3
|
+
enola_intent:
|
|
4
|
+
governs:
|
|
5
|
+
- app/controllers/api/**
|
|
6
|
+
decisions:
|
|
7
|
+
- every route under /api has at least one consumer in shop-ui
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Public surface
|
|
11
|
+
|
|
12
|
+
Fourteen routes under `/api`, all matched by a consumer in `shop-ui`
|
|
13
|
+
(coverage report, receipt shop@3f2a9c1). A route that stops matching is
|
|
14
|
+
a finding of `unused-routes`, judged in the ledger before any removal.
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# A knowledge base with enola
|
|
2
|
+
|
|
3
|
+
A team that keeps decisions beside its code (a wiki, ADRs, a design log)
|
|
4
|
+
can compile them into the graph. Then a file answers which decision
|
|
5
|
+
governs it, a decision answers which code it covers, a structural claim
|
|
6
|
+
in prose can be re-checked mechanically, and a finding judged once stays
|
|
7
|
+
judged. This guide is that practice, as it runs day to day on a
|
|
8
|
+
twenty-repository knowledge base.
|
|
9
|
+
|
|
10
|
+
## Pages compile into the graph
|
|
11
|
+
|
|
12
|
+
A markdown page carrying an `enola_intent:` block is an intent fact when
|
|
13
|
+
the snapshot is taken, and every anchor on it is an edge to a file:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
---
|
|
17
|
+
title: Charges run through one service
|
|
18
|
+
enola_intent:
|
|
19
|
+
page:
|
|
20
|
+
type: decision
|
|
21
|
+
status: accepted
|
|
22
|
+
scope:
|
|
23
|
+
- shop
|
|
24
|
+
relations:
|
|
25
|
+
- rel: supersedes
|
|
26
|
+
to: wiki/adrs/charges-in-controllers.md
|
|
27
|
+
anchors:
|
|
28
|
+
- repo: shop
|
|
29
|
+
path: app/billing/charge.rb
|
|
30
|
+
- repo: shop
|
|
31
|
+
path: app/billing/refund.rb
|
|
32
|
+
---
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Derive the block rather than hand-maintain it: if your pages already cite
|
|
36
|
+
the code they describe (`sources:` lines naming repository paths), the
|
|
37
|
+
anchors are those citations, and a small script stamps the block from
|
|
38
|
+
them and checks the derivation in CI. enola validates the block; keeping
|
|
39
|
+
it truthful to your conventions is yours.
|
|
40
|
+
|
|
41
|
+
Index the knowledge base as a member of the cluster, last, so its pages
|
|
42
|
+
compile over the code they anchor:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
repos:
|
|
46
|
+
- ../shop
|
|
47
|
+
- ../mobile
|
|
48
|
+
- .
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Ask the graph what governs a file
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
enola plan -paths app/billing/charge.rb .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
and over MCP, `governing_intent` for a file or a symbol: the pages whose
|
|
58
|
+
anchors cover it, with their relation trails (what supersedes what, what
|
|
59
|
+
depends on what), and for a page, which code its anchors cover. A file no
|
|
60
|
+
page governs is an answer too, and the difference between "no page
|
|
61
|
+
governs this" and "the graph was not asked" must stay visible: a missing
|
|
62
|
+
snapshot is a named skip, never silence.
|
|
63
|
+
|
|
64
|
+
This is the call behind the [intent-first gate](10-hooks.md): before an
|
|
65
|
+
agent's first edit into a repository, the governing pages are read.
|
|
66
|
+
|
|
67
|
+
## Laws over the pages
|
|
68
|
+
|
|
69
|
+
Because anchors are facts, laws can read them (see [writing
|
|
70
|
+
laws](04-writing-laws.md)):
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
part :billing, files: "app/billing/**"
|
|
74
|
+
part :old_way, governed_by: "wiki/adrs/*.md status:superseded"
|
|
75
|
+
part :new_way, governed_by: "wiki/adrs/charges-in-one-service.md"
|
|
76
|
+
|
|
77
|
+
law "billing is documented" do
|
|
78
|
+
billing.must_be_governed
|
|
79
|
+
why "code under a decision needs the page that decided it"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
law "the new way never reaches the old" do
|
|
83
|
+
new_way.must_not_call :old_way
|
|
84
|
+
why "a superseded decision's code is on its way out, not a dependency"
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Receipts are the committed state
|
|
89
|
+
|
|
90
|
+
Graph artifacts (`.enola/`, the cluster's output directory) are
|
|
91
|
+
machine-local and ignored. What enters version control is the receipt:
|
|
92
|
+
one entry per repository with the snapshot's fingerprint, the commit it
|
|
93
|
+
was taken at, the enola version and the providers that ran. Keep them in
|
|
94
|
+
one file the knowledge base owns:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{"repos": {"shop": {"git_commit": "…", "content_digest": "sha256:…", "enola_version": "0.5.0"}}}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Two rules follow. **Regenerate the whole cluster, never one member**: a
|
|
101
|
+
snapshot scoped to one repository replaces the union rather than
|
|
102
|
+
updating its slice, and everything that reads the union then answers
|
|
103
|
+
from a narrower world without saying so. **Not comparable is not a
|
|
104
|
+
pass**: when a member's snapshot changed enola version or provider set,
|
|
105
|
+
the grade declines; re-pin and regenerate rather than reading silence as
|
|
106
|
+
clean.
|
|
107
|
+
|
|
108
|
+
## Citations with a grammar
|
|
109
|
+
|
|
110
|
+
A structural claim in prose ("`internal/facts` has 157 dependents")
|
|
111
|
+
cites the receipt it was read from, in one fixed shape, so a script can
|
|
112
|
+
re-check it against the committed receipts and flag the ones whose
|
|
113
|
+
digest moved. The shape is yours to choose; what matters is that there
|
|
114
|
+
is one, that one extractor parses it, and that a periodic sweep lists
|
|
115
|
+
every citation with a verdict (verified, stale, malformed). A claim
|
|
116
|
+
nobody can re-check is a sentence, not a fact.
|
|
117
|
+
|
|
118
|
+
## Findings are judged once
|
|
119
|
+
|
|
120
|
+
The explainers report candidates, not verdicts: a cycle at 0.7
|
|
121
|
+
confidence, a god class, an unused route. Before a finding grounds a
|
|
122
|
+
page, confirm it against the code and record the judgment in a ledger
|
|
123
|
+
beside the receipts, one entry per finding signature: accepted (and
|
|
124
|
+
where it landed), rejected, or permanent noise. The next session reads
|
|
125
|
+
the ledger instead of re-deciding, and the periodic sweep reports a
|
|
126
|
+
ledger entry whose finding no longer exists, which is the code moving
|
|
127
|
+
past a recorded judgment.
|
|
128
|
+
|
|
129
|
+
## Pulled, with one push
|
|
130
|
+
|
|
131
|
+
Skills fetch the graph while already reasoning about something: a
|
|
132
|
+
deepdive asks `impact_analysis` and `governing_intent`, an ingest checks
|
|
133
|
+
a claim with `query_facts` while the source is open, a drift sweep
|
|
134
|
+
regenerates and diffs receipts. Nothing lands on a board because the
|
|
135
|
+
graph said so. The one exception is the session-end hook, which speaks
|
|
136
|
+
only when the session's change introduced a structural regression, or
|
|
137
|
+
when it could not grade at all; a gate nobody remembers to run is no gate.
|
|
138
|
+
|
|
139
|
+
## The wrapper
|
|
140
|
+
|
|
141
|
+
Every skill calls a thin command the knowledge base owns rather than
|
|
142
|
+
the binary: `kb enola generate | receipt | diff | findings | judge | impact
|
|
143
|
+
| govern | citations | baseline | check | coverage | doctor`. It resolves
|
|
144
|
+
the binary, the cluster config and the receipts file, and when any is
|
|
145
|
+
absent it prints one line saying so and exits 0. That is what keeps a
|
|
146
|
+
skill from failing on a machine without enola, and what keeps "the graph
|
|
147
|
+
was not asked" distinguishable from "the graph agreed". The [wrapper
|
|
148
|
+
guide](12-the-wrapper.md) has its shape.
|
data/guides/10-hooks.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Hooks: the graph at the moments that matter
|
|
2
|
+
|
|
3
|
+
A hook fires at a moment nobody has to remember: a session starting, the
|
|
4
|
+
first edit into a repository, a turn ending, a push. Each one here reads
|
|
5
|
+
the graph at that moment, blocks at most once, and forces a reading,
|
|
6
|
+
never a verdict. The scripts are in [`templates/hooks/`](../templates/hooks);
|
|
7
|
+
they are written for Claude Code's hook protocol (JSON on stdin, exit 2
|
|
8
|
+
to block with the message on stderr) and translate to any agent with
|
|
9
|
+
hooks.
|
|
10
|
+
|
|
11
|
+
## enola's own pair
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
enola install --hooks
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
wires two: **session start** restores the snapshot and serves it over
|
|
18
|
+
MCP; **stop** grades the session's delta against the pinned baseline and
|
|
19
|
+
speaks only when the change introduced a regression or could not be
|
|
20
|
+
compared. `enola doctor` reports when each last fired and what it
|
|
21
|
+
concluded; a hook that never fires is the failure to look for.
|
|
22
|
+
|
|
23
|
+
## The intent-first gate (PreToolUse on Edit and Write)
|
|
24
|
+
|
|
25
|
+
The first write into each repository per piece of work is blocked once.
|
|
26
|
+
The block carries two things: the pages that govern the file
|
|
27
|
+
(`governing_intent`, or `enola plan -paths <file>` when the MCP is not
|
|
28
|
+
in reach) and the pre-edit contract (the declared laws binding the path
|
|
29
|
+
and the blast radius over the current snapshot). The agent reads, then
|
|
30
|
+
retries, and the retry proceeds. The gate re-arms on every user message,
|
|
31
|
+
so each ask gets its own forced stop and no piece of work gets two.
|
|
32
|
+
|
|
33
|
+
What it refuses to do: decide. If the governing page contradicts the
|
|
34
|
+
intended change, the contract says amend the page first; the hook only
|
|
35
|
+
makes the page impossible to miss. When no page governs the file, it
|
|
36
|
+
says so and asks whether the work should be shaped. When the graph is
|
|
37
|
+
absent, it says so and lets the write through; a missing graph is a
|
|
38
|
+
named skip, never a wall.
|
|
39
|
+
|
|
40
|
+
`templates/hooks/intent-first-gate.sh` gates Edit and Write. The brain's
|
|
41
|
+
own gate also classifies shell commands as writes (a `sed -i`, a
|
|
42
|
+
redirect, a `cp` into the repository); the template marks where that
|
|
43
|
+
classifier goes and leaves it out, because the rules for what counts as a
|
|
44
|
+
write depend on your tree.
|
|
45
|
+
|
|
46
|
+
## The stop-time debt (Stop)
|
|
47
|
+
|
|
48
|
+
When the turn ends, two debts can block it once each:
|
|
49
|
+
|
|
50
|
+
- **Code moved, no spec moved.** Files changed in a governed repository
|
|
51
|
+
this turn and no governing page did. The block asks for either the
|
|
52
|
+
amendment or an explicit sentence, in the reply, saying why the pages
|
|
53
|
+
as written already cover the change.
|
|
54
|
+
- **A push went out with no grade.** A `git push` to a repository
|
|
55
|
+
happened this turn and no `enola check` ran. The block asks for the
|
|
56
|
+
grade, read, before finishing.
|
|
57
|
+
|
|
58
|
+
Both read a small per-session record the PostToolUse hook writes
|
|
59
|
+
(`templates/hooks/record.sh`): which files moved, whether a push
|
|
60
|
+
happened, whether a check ran. The debt hook is the mechanical half of
|
|
61
|
+
two rules a team can otherwise only ask people to remember.
|
|
62
|
+
|
|
63
|
+
## The union refresh (SessionStart)
|
|
64
|
+
|
|
65
|
+
When the session starts, compare the paired repository's HEAD with the
|
|
66
|
+
commit the committed receipt records. When they differ, regenerate that
|
|
67
|
+
member's slice of the union in the background and say so in one line;
|
|
68
|
+
when they agree, say that. The agent's first structural question then
|
|
69
|
+
reads a graph that matches the checkout it is about to edit.
|
|
70
|
+
|
|
71
|
+
## Wiring
|
|
72
|
+
|
|
73
|
+
Claude Code, in `.claude/settings.json`:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"hooks": {
|
|
78
|
+
"SessionStart": [{"matcher": "startup|resume", "hooks": [
|
|
79
|
+
{"type": "command", "command": "enola hook session-start"},
|
|
80
|
+
{"type": "command", "command": "\"${CLAUDE_PROJECT_DIR:-.}/hooks/session-refresh.sh\""}]}],
|
|
81
|
+
"PreToolUse": [{"matcher": "Edit|Write|MultiEdit", "hooks": [
|
|
82
|
+
{"type": "command", "command": "\"${CLAUDE_PROJECT_DIR:-.}/hooks/intent-first-gate.sh\""}]}],
|
|
83
|
+
"PostToolUse": [{"matcher": "Edit|Write|MultiEdit|Bash", "hooks": [
|
|
84
|
+
{"type": "command", "command": "\"${CLAUDE_PROJECT_DIR:-.}/hooks/record.sh\""}]}],
|
|
85
|
+
"UserPromptSubmit": [{"hooks": [
|
|
86
|
+
{"type": "command", "command": "\"${CLAUDE_PROJECT_DIR:-.}/hooks/record.sh\" reset"}]}],
|
|
87
|
+
"Stop": [{"hooks": [
|
|
88
|
+
{"type": "command", "command": "enola hook stop"},
|
|
89
|
+
{"type": "command", "command": "\"${CLAUDE_PROJECT_DIR:-.}/hooks/stop-debt.sh\""}]}]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## What it feels like
|
|
95
|
+
|
|
96
|
+
An agent asked to change a billing service edits nothing until it has
|
|
97
|
+
read the decision that put charges through one service; it grades the
|
|
98
|
+
change before the push and writes the grade into the pull request; and
|
|
99
|
+
when it ends the turn having changed code under a decision without
|
|
100
|
+
touching the decision, it is asked to account for that once. None of
|
|
101
|
+
this needs the agent to know enola exists.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# The skills, as they run
|
|
2
|
+
|
|
3
|
+
A skill is a named piece of practice an agent follows. Each of these asks
|
|
4
|
+
the graph one specific question at one specific moment; the value is the
|
|
5
|
+
moment, not the prompt. They are described here as they run on a
|
|
6
|
+
twenty-repository knowledge base, and shipped under
|
|
7
|
+
[`templates/skills/`](../templates/skills) to copy.
|
|
8
|
+
|
|
9
|
+
## Before shaping work: the deepdive (`enola-deepdive`)
|
|
10
|
+
|
|
11
|
+
Shaping a change starts with three graph questions, answered before a
|
|
12
|
+
line of the plan is written:
|
|
13
|
+
|
|
14
|
+
- `impact_analysis` on every symbol the work would change: fan-in,
|
|
15
|
+
fan-out, named callers. A symbol with 157 dependents is a different
|
|
16
|
+
plan from one with 3.
|
|
17
|
+
- `governing_intent` on the paths: which decisions already cover the
|
|
18
|
+
area, so the plan amends a page rather than contradicting it.
|
|
19
|
+
- `query_insights` for the area: what the explainers already computed (a
|
|
20
|
+
cycle someone must have introduced, a layer crossed, a dead route), so
|
|
21
|
+
the plan inherits judged findings instead of rediscovering them.
|
|
22
|
+
|
|
23
|
+
The plan cites the receipt it read from, so a later reader can re-check
|
|
24
|
+
the numbers.
|
|
25
|
+
|
|
26
|
+
## Before building: the grade (`enola-grade-before-push`)
|
|
27
|
+
|
|
28
|
+
`set_baseline` once, early; after the change, `generate_snapshot` and
|
|
29
|
+
`diff_snapshot`; the delta (introduced, resolved, edges added) goes into
|
|
30
|
+
the pull request in one sentence. A declined comparison is not a pass.
|
|
31
|
+
This is also the [stop-time debt](10-hooks.md) hook's demand.
|
|
32
|
+
|
|
33
|
+
## While ingesting a source: the claim check (`enola-ingest-check`)
|
|
34
|
+
|
|
35
|
+
A page that states a structural fact (a stack, a public surface, a
|
|
36
|
+
route, a table) checks it while the source is open: `query_facts` for
|
|
37
|
+
the fact, `impact_analysis` for the count. A wrong claim ingested today
|
|
38
|
+
is cited by a page tomorrow and reasoned from by an agent next week, so
|
|
39
|
+
verification is cheapest now. For an incremental ingest, each changed
|
|
40
|
+
path runs through `governing_intent`: the pages whose anchors cover a
|
|
41
|
+
changed file are exactly the synthesis that may have drifted, and a
|
|
42
|
+
changed file no page governs is a gap the ingest may be about to fill.
|
|
43
|
+
|
|
44
|
+
## The periodic sweep: drift (`enola-drift`)
|
|
45
|
+
|
|
46
|
+
On a schedule, or before publishing: regenerate the cluster, diff the
|
|
47
|
+
committed receipts (fingerprint and commit per member), and read
|
|
48
|
+
`coverage_report` for which cross-repository edges resolved and which did
|
|
49
|
+
not. Receipts that moved name the members whose pages want a re-read.
|
|
50
|
+
Not a comparison of findings between two runs; that is the grade's job.
|
|
51
|
+
|
|
52
|
+
## Knowledge hygiene: citations and the ledger (`enola-citation-hygiene`)
|
|
53
|
+
|
|
54
|
+
Every receipt citation in prose gets a verdict against the committed
|
|
55
|
+
receipts: verified, stale (the digest moved), malformed. Stale ones are
|
|
56
|
+
re-read, not rewritten. The finding ledger is swept the same way: an
|
|
57
|
+
accepted finding whose evidence no longer exists in the graph is the
|
|
58
|
+
code moving past a judgment, and the page that cites it is re-read.
|
|
59
|
+
|
|
60
|
+
## Stepping back: the blast radius (`enola-zoom-out`)
|
|
61
|
+
|
|
62
|
+
When deep in a change, one call answers how it sits in the whole:
|
|
63
|
+
`impact_analysis` for the symbols in hand, `find_path` between the
|
|
64
|
+
part being changed and the parts that consume it, `governing_intent`
|
|
65
|
+
for the decisions around it. The answer is one paragraph of technical
|
|
66
|
+
fit the agent can stand on, beside the product fit it reasons out.
|
|
67
|
+
|
|
68
|
+
## Writing and explaining laws (`enola-laws`)
|
|
69
|
+
|
|
70
|
+
Covered in [writing laws](04-writing-laws.md): lint before verdict,
|
|
71
|
+
`constraints explain` before arguing with a verdict, ratchet before
|
|
72
|
+
strict.
|
|
73
|
+
|
|
74
|
+
## What makes these skills and not prompts
|
|
75
|
+
|
|
76
|
+
Each names the call, the moment, and the refusal: what the agent does
|
|
77
|
+
when the graph is absent (say so, proceed, never pretend), when the
|
|
78
|
+
comparison declines (re-pin, never read as clean), when a finding is a
|
|
79
|
+
candidate (confirm against the code, judge once, record). That is the
|
|
80
|
+
practice the hooks enforce and the skills describe; the two are one
|
|
81
|
+
contract read from two sides.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# The wrapper
|
|
2
|
+
|
|
3
|
+
Skills call a command the knowledge base owns, not the binary. The
|
|
4
|
+
wrapper is thin: it knows where the binary, the cluster config and the
|
|
5
|
+
receipts file live, it records receipts after a generate, and when
|
|
6
|
+
anything is absent it prints one line saying so and exits 0. That is the
|
|
7
|
+
whole design, and it is what lets every skill run on a machine without
|
|
8
|
+
enola without failing, while keeping "the graph was not asked" a
|
|
9
|
+
different sentence from "the graph agreed".
|
|
10
|
+
|
|
11
|
+
## The operations
|
|
12
|
+
|
|
13
|
+
| Op | Answers |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `generate` | snapshot the cluster, then record every member's receipt |
|
|
16
|
+
| `receipt [member]` | what was recorded |
|
|
17
|
+
| `diff` | which members' live receipts drifted from the recorded ones |
|
|
18
|
+
| `findings [--repo] [--all]` | the explainers' findings across the cluster, attributed to the member their evidence names, with judged ones hidden |
|
|
19
|
+
| `judge <signature> accepted\|rejected\|noise` | record a verdict in the ledger |
|
|
20
|
+
| `impact <symbol>` | fan-in, fan-out, named callers, read from the on-disk facts |
|
|
21
|
+
| `govern <path or page>` | the pages governing a file, or the code a page anchors |
|
|
22
|
+
| `citations` | every receipt citation in prose with its verdict |
|
|
23
|
+
| `baseline pin\|show\|clear [member]` | the "before" a change is graded against |
|
|
24
|
+
| `check [member]` | the grade, reported, never gating |
|
|
25
|
+
| `coverage` | which cross-repository edges resolved |
|
|
26
|
+
| `doctor` | whether the hooks fire |
|
|
27
|
+
|
|
28
|
+
## The shape, in any language
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
resolve binary -> absent: print "enola: not installed, the graph was not asked"; exit 0
|
|
32
|
+
resolve config -> absent: print the path looked for; exit 0
|
|
33
|
+
resolve receipts -> absent on a read op: print; exit 0
|
|
34
|
+
run the op -> on generate, read each member's .enola/receipt.json
|
|
35
|
+
and write fingerprint, commit, version, providers
|
|
36
|
+
into the receipts file
|
|
37
|
+
exit codes -> the binary's, except check: report, exit 0
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Three things to keep:
|
|
41
|
+
|
|
42
|
+
- **Receipts are the only committed state.** `generate` writes them;
|
|
43
|
+
nothing else does. `diff` compares them to the live ones; it is not a
|
|
44
|
+
comparison of findings.
|
|
45
|
+
- **Findings read from disk, not the MCP server.** `impact` and
|
|
46
|
+
`findings` parse the fact and insight files, so scheduled and headless
|
|
47
|
+
runs keep working when no server is up.
|
|
48
|
+
- **`check` reports; it never gates.** The binary exits 1 on a
|
|
49
|
+
regression and the wrapper does not propagate it: no graph-dependent
|
|
50
|
+
step may stop work, so the verdict is surfaced for a person to weigh.
|
|
51
|
+
Exit 3 from the binary means the snapshots were not comparable; the
|
|
52
|
+
wrapper prints that as "not asked", never as a pass.
|
|
53
|
+
|
|
54
|
+
## The ledger beside the receipts
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{"findings": {"cycles|app/jobs -> app/models -> app/jobs": {"verdict": "accepted", "at": "2026-08-22", "reason": "real; the mailer path", "landed": "wiki/shop/permanent/architecture.md"}}}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
One entry per finding signature, written on judgment. There is no
|
|
61
|
+
pending state: nothing enumerates unjudged findings as work, because a
|
|
62
|
+
finding nobody bet on is an observation.
|
|
63
|
+
|
|
64
|
+
## Why not call the binary directly
|
|
65
|
+
|
|
66
|
+
Every skill would then carry the absent-binary branch, the config
|
|
67
|
+
resolution and the receipt bookkeeping, and they would drift. The
|
|
68
|
+
wrapper is where those live once, which is why the guides say `kb enola
|
|
69
|
+
impact` where the brain that runs this says `brain.py enola impact`.
|
data/lib/enola-guides.rb
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse on Edit|Write|MultiEdit: block the first write into each
|
|
3
|
+
# repository per piece of work, once, with the governing pages and the
|
|
4
|
+
# pre-edit contract on stderr. The retry proceeds; the gate forces the
|
|
5
|
+
# reading, never the verdict. Re-armed by record.sh on every user message.
|
|
6
|
+
#
|
|
7
|
+
# Where a shell-command classifier would go: a Bash tool call whose text
|
|
8
|
+
# carries a write shape (sed -i, a redirect, tee, cp or mv into the tree,
|
|
9
|
+
# an interpreter fed a heredoc) is a write too. What counts depends on your
|
|
10
|
+
# tree; gate it the same way once you have decided.
|
|
11
|
+
set -u
|
|
12
|
+
input=$(cat 2>/dev/null || true)
|
|
13
|
+
sid=$(jq -r '.session_id // "nosession"' <<<"$input" 2>/dev/null)
|
|
14
|
+
file=$(jq -r '.tool_input.file_path // empty' <<<"$input" 2>/dev/null)
|
|
15
|
+
[ -n "$file" ] || exit 0
|
|
16
|
+
|
|
17
|
+
top=$(git -C "$(dirname "$file")" rev-parse --show-toplevel 2>/dev/null) || exit 0
|
|
18
|
+
case "$file" in
|
|
19
|
+
"$top"/.enola/*|"$top"/wiki/_views/*|"$top"/wiki/_state/*) exit 0 ;;
|
|
20
|
+
esac
|
|
21
|
+
rel="${file#"$top"/}"
|
|
22
|
+
repo=$(basename "$top")
|
|
23
|
+
|
|
24
|
+
state="${TMPDIR:-/tmp}/intent-first-$(id -u)"
|
|
25
|
+
mkdir -p "$state"
|
|
26
|
+
ack="$state/${sid}-${repo}.ack"
|
|
27
|
+
[ -e "$ack" ] && exit 0
|
|
28
|
+
touch "$ack"
|
|
29
|
+
|
|
30
|
+
govern=""
|
|
31
|
+
if command -v enola >/dev/null 2>&1; then
|
|
32
|
+
govern=$(cd "$top" && timeout 30 enola plan -paths "$rel" . 2>/dev/null | sed -n '/^Plan report/,$p' | head -40)
|
|
33
|
+
fi
|
|
34
|
+
[ -n "$govern" ] || govern="(enola plan unavailable: no binary or no snapshot for this checkout; named skip, consult the decision records by hand)"
|
|
35
|
+
|
|
36
|
+
cat >&2 <<EOF
|
|
37
|
+
intent-first gate (${repo}): first write for this piece of work.
|
|
38
|
+
|
|
39
|
+
Before ${rel} moves, read what governs it and check the intended change
|
|
40
|
+
against it. If the change deviates from what the governing page records,
|
|
41
|
+
amend the page first; if nothing governs this work, say so and ask
|
|
42
|
+
whether it should be shaped.
|
|
43
|
+
|
|
44
|
+
${govern}
|
|
45
|
+
|
|
46
|
+
Retrying proceeds. This gate re-arms on every user message; within one
|
|
47
|
+
piece of work it will not fire again for ${repo}.
|
|
48
|
+
EOF
|
|
49
|
+
exit 2
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse on Edit|Write|MultiEdit|Bash: record what this turn did, so
|
|
3
|
+
# the stop-time debt hook can account for it. With "reset" as the first
|
|
4
|
+
# argument (UserPromptSubmit): a new user message begins a new piece of
|
|
5
|
+
# work, so clear the record and the gate's acknowledgements.
|
|
6
|
+
set -u
|
|
7
|
+
state="${TMPDIR:-/tmp}/intent-first-$(id -u)"
|
|
8
|
+
mkdir -p "$state"
|
|
9
|
+
input=$(cat 2>/dev/null || true)
|
|
10
|
+
sid=$(jq -r '.session_id // "nosession"' <<<"$input" 2>/dev/null)
|
|
11
|
+
turn="$state/${sid}.turn"
|
|
12
|
+
|
|
13
|
+
if [ "${1:-}" = "reset" ]; then
|
|
14
|
+
rm -f "$state/${sid}"-*.ack "$turn" "$state/${sid}.debt-blocked" "$state/${sid}.push-blocked"
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
record() { printf '%s\n' "$1" >> "$turn"; }
|
|
19
|
+
|
|
20
|
+
tool=$(jq -r '.tool_name // empty' <<<"$input" 2>/dev/null)
|
|
21
|
+
if [ "$tool" = "Bash" ]; then
|
|
22
|
+
cmd=$(jq -r '.tool_input.command // empty' <<<"$input" 2>/dev/null)
|
|
23
|
+
[ -n "$cmd" ] || exit 0
|
|
24
|
+
printf '%s' "$cmd" | grep -qE '(^|[[:space:];&|])git[[:space:]]+push' && record "push"
|
|
25
|
+
printf '%s' "$cmd" | grep -qE 'enola[[:space:]]+check' && record "check"
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
file=$(jq -r '.tool_input.file_path // .tool_response.filePath // empty' <<<"$input" 2>/dev/null)
|
|
30
|
+
[ -n "$file" ] || exit 0
|
|
31
|
+
top=$(git -C "$(dirname "$file")" rev-parse --show-toplevel 2>/dev/null) || exit 0
|
|
32
|
+
rel="${file#"$top"/}"
|
|
33
|
+
case "$rel" in
|
|
34
|
+
wiki/*.md|docs/decisions/*.md|docs/adrs/*.md) record "spec $rel" ;;
|
|
35
|
+
*) record "code $rel" ;;
|
|
36
|
+
esac
|
|
37
|
+
exit 0
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SessionStart: when a cluster member's HEAD moved past the commit the
|
|
3
|
+
# committed receipts record, regenerate the cluster in the background and
|
|
4
|
+
# say so in one line; when they agree, say that. The agent's first
|
|
5
|
+
# structural question then reads a graph that matches the checkout.
|
|
6
|
+
#
|
|
7
|
+
# RECEIPTS is the knowledge base's committed receipts file; CLUSTER the
|
|
8
|
+
# cluster config; MEMBER the repository to compare (default: this one).
|
|
9
|
+
set -u
|
|
10
|
+
RECEIPTS="${RECEIPTS:-wiki/_state/enola/receipts.json}"
|
|
11
|
+
CLUSTER="${CLUSTER:-enola-cluster.yaml}"
|
|
12
|
+
MEMBER="${MEMBER:-.}"
|
|
13
|
+
command -v enola >/dev/null 2>&1 || { echo "enola hooks: no binary on PATH; the graph was not asked (named skip)"; exit 0; }
|
|
14
|
+
[ -f "$RECEIPTS" ] || { echo "enola hooks: no receipts at $RECEIPTS; the graph was not asked (named skip)"; exit 0; }
|
|
15
|
+
|
|
16
|
+
label=$(basename "$(cd "$MEMBER" && pwd -P)")
|
|
17
|
+
head=$(git -C "$MEMBER" rev-parse HEAD 2>/dev/null || true)
|
|
18
|
+
recorded=$(python3 - "$RECEIPTS" "$label" <<'PY' 2>/dev/null
|
|
19
|
+
import json, sys
|
|
20
|
+
r = json.load(open(sys.argv[1]))
|
|
21
|
+
print(((r.get("repos") or r).get(sys.argv[2]) or {}).get("git_commit") or "")
|
|
22
|
+
PY
|
|
23
|
+
)
|
|
24
|
+
if [ -n "$head" ] && [ "$head" != "$recorded" ]; then
|
|
25
|
+
log="${TMPDIR:-/tmp}/enola-refresh-$(id -u).log"
|
|
26
|
+
(nohup enola --generate "$CLUSTER" > "$log" 2>&1 &)
|
|
27
|
+
echo "enola hooks: $label is at ${head:0:11}, the recorded receipt at ${recorded:0:11}; regenerating the cluster in the background (log $log)."
|
|
28
|
+
else
|
|
29
|
+
echo "enola hooks: the recorded receipt matches $label at ${head:0:11}."
|
|
30
|
+
fi
|
|
31
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Stop: block the end of the turn once when this turn pushed without a
|
|
3
|
+
# grade, and once when code moved under decision records and no record
|
|
4
|
+
# moved. Reads the per-session record written by record.sh.
|
|
5
|
+
set -u
|
|
6
|
+
state="${TMPDIR:-/tmp}/intent-first-$(id -u)"
|
|
7
|
+
input=$(cat 2>/dev/null || true)
|
|
8
|
+
sid=$(jq -r '.session_id // "nosession"' <<<"$input" 2>/dev/null)
|
|
9
|
+
turn="$state/${sid}.turn"
|
|
10
|
+
[ -f "$turn" ] || exit 0
|
|
11
|
+
|
|
12
|
+
if grep -q '^push$' "$turn" && ! grep -q '^check$' "$turn" && [ ! -e "$state/${sid}.push-blocked" ]; then
|
|
13
|
+
touch "$state/${sid}.push-blocked"
|
|
14
|
+
cat >&2 <<'EOF'
|
|
15
|
+
push without a grade: this turn pushed and ran no enola check.
|
|
16
|
+
|
|
17
|
+
Before finishing: run `enola check <repo>` against the pinned baseline
|
|
18
|
+
and read the grade. A declined grade is not a pass; re-pin and grade
|
|
19
|
+
again. This check blocks once per turn.
|
|
20
|
+
EOF
|
|
21
|
+
exit 2
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if grep -q '^code' "$turn" && ! grep -q '^spec' "$turn" && [ ! -e "$state/${sid}.debt-blocked" ]; then
|
|
25
|
+
touch "$state/${sid}.debt-blocked"
|
|
26
|
+
summary=$(awk -F'\t' '$1 == "code" { print " " $2 }' "$turn" | sort -u | head -15)
|
|
27
|
+
cat >&2 <<EOF
|
|
28
|
+
intent debt: code moved this turn, and no decision record did.
|
|
29
|
+
|
|
30
|
+
Changed without a record moving:
|
|
31
|
+
${summary}
|
|
32
|
+
|
|
33
|
+
Before finishing: either amend the governing record to match what was
|
|
34
|
+
built, or state explicitly, in your reply, why the records as written
|
|
35
|
+
already cover the change. This check blocks once per turn.
|
|
36
|
+
EOF
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enola-citation-hygiene
|
|
3
|
+
description: Use during grooming; gives every receipt citation in prose a verdict against the committed receipts and sweeps the finding ledger for judgments the code has moved past
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# enola-citation-hygiene: every citation gets a verdict
|
|
7
|
+
|
|
8
|
+
1. `kb enola citations`: each receipt citation in prose is verified, stale
|
|
9
|
+
(the digest moved) or malformed.
|
|
10
|
+
2. Stale: re-read the cited page against the current facts and amend the
|
|
11
|
+
sentence; never update the citation alone, the sentence is what may be
|
|
12
|
+
wrong.
|
|
13
|
+
3. Malformed: fix the citation to the recorded form.
|
|
14
|
+
4. `kb enola findings --all`: for each accepted finding in the ledger,
|
|
15
|
+
confirm its evidence still exists in the graph. Evidence gone means
|
|
16
|
+
the code moved past the judgment; re-read the page that landed it and
|
|
17
|
+
record the new state.
|
|
18
|
+
5. Rejected and noise verdicts stay; they keep the finding hidden on
|
|
19
|
+
purpose.
|
|
20
|
+
|
|
21
|
+
A citation is a promise that a number was read, not remembered; this
|
|
22
|
+
skill keeps the promise current.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enola-deepdive
|
|
3
|
+
description: Use before shaping or planning a change; asks the graph three questions (impact, governing decisions, existing findings) and writes the answers into the plan with the receipt they came from
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# enola-deepdive: ask the graph before writing the plan
|
|
7
|
+
|
|
8
|
+
1. Name the symbols the change would touch. For each:
|
|
9
|
+
`kb enola impact <Symbol>` (or `impact_analysis` over MCP). Record fan-in,
|
|
10
|
+
fan-out and the named callers. A symbol with 157 dependents is a
|
|
11
|
+
different plan from one with 3.
|
|
12
|
+
2. For the paths: `kb enola govern <path>` (`governing_intent`). The pages
|
|
13
|
+
returned are the decisions already covering the area; the plan amends
|
|
14
|
+
one of them, it does not contradict it in silence.
|
|
15
|
+
3. For the area: `query_insights` filtered to it. Cycles, layer crossings,
|
|
16
|
+
dead routes and hotspots are already computed and some are already
|
|
17
|
+
judged in the ledger; inherit them.
|
|
18
|
+
4. Write the three answers into the plan, each with the receipt
|
|
19
|
+
(`kb enola receipt <member>`) it was read from.
|
|
20
|
+
5. If the graph is absent or the receipts are stale (`kb enola diff`), say
|
|
21
|
+
so in the plan and proceed; never fill the numbers from memory.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enola-drift
|
|
3
|
+
description: Use on a schedule or before publishing; regenerates the cluster, compares committed receipts to live ones, and names the members whose pages want a re-read
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# enola-drift: which members moved
|
|
7
|
+
|
|
8
|
+
1. `kb enola generate` to regenerate the cluster and record receipts.
|
|
9
|
+
2. `kb enola diff`: each member whose fingerprint or commit moved is
|
|
10
|
+
listed. Those are the members whose pages may describe a structure that
|
|
11
|
+
no longer exists.
|
|
12
|
+
3. `kb enola coverage` (`coverage_report`): which cross-repository edges
|
|
13
|
+
resolved and which did not. An edge that stopped resolving is a
|
|
14
|
+
consumer a page may still claim.
|
|
15
|
+
4. For each moved member: `kb enola govern <member path>` and re-read the
|
|
16
|
+
pages returned against the new facts. Amend what changed; leave what
|
|
17
|
+
did not.
|
|
18
|
+
5. Commit the receipts with the page amendments, in one commit.
|
|
19
|
+
|
|
20
|
+
This is a comparison of receipts, not of findings. Findings between two
|
|
21
|
+
runs are the grade's job (`enola-grade-before-push`).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enola-ingest-check
|
|
3
|
+
description: Use while ingesting a source into the knowledge base; checks every structural claim against the graph while the source is open, and routes changed paths to the pages that govern them
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# enola-ingest-check: verify the claim while the source is open
|
|
7
|
+
|
|
8
|
+
1. When a page is about to state a structural fact (a stack, a public
|
|
9
|
+
surface, a route, a table, a count): `query_facts` for the fact,
|
|
10
|
+
`kb enola impact <Symbol>` for the count. Write what the graph says,
|
|
11
|
+
and cite the receipt.
|
|
12
|
+
2. For an incremental ingest, for each changed path:
|
|
13
|
+
`kb enola govern <path>`. The pages returned are the synthesis that may
|
|
14
|
+
have drifted; re-read them against the change. A changed path no page
|
|
15
|
+
governs is a gap the ingest is about to fill, so anchor the new page to
|
|
16
|
+
it with `enola_intent`.
|
|
17
|
+
3. A claim the graph cannot answer (product intent, history, a reason) is
|
|
18
|
+
stated as the source's claim, not as a fact of the code.
|
|
19
|
+
4. If the graph is absent, the page says the claim is unverified; it does
|
|
20
|
+
not silently become a fact.
|
|
21
|
+
|
|
22
|
+
A wrong claim ingested today is cited tomorrow and reasoned from next
|
|
23
|
+
week; verification is cheapest now.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enola-zoom-out
|
|
3
|
+
description: Use when deep in a change and the question is how it sits in the whole; answers the technical blast radius in one paragraph from the graph, beside the product fit reasoned out
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# enola-zoom-out: the blast radius in one paragraph
|
|
7
|
+
|
|
8
|
+
1. `kb enola impact <Symbol>` for the symbols in hand: fan-in, named callers.
|
|
9
|
+
2. `find_path` from the part being changed to the parts that consume it:
|
|
10
|
+
the route the change travels.
|
|
11
|
+
3. `kb enola govern <path>`: the decisions around it.
|
|
12
|
+
4. Write one paragraph of technical fit: what depends on this, through
|
|
13
|
+
what, under which decisions. Put it beside the product fit, which the
|
|
14
|
+
graph cannot answer.
|
|
15
|
+
5. If the paragraph names a dependent the plan did not, the plan is
|
|
16
|
+
incomplete; return to `enola-deepdive`.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: enola-guides
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Muhamed Isabegovic
|
|
@@ -25,6 +25,12 @@ files:
|
|
|
25
25
|
- README.md
|
|
26
26
|
- examples/ember-app/enola/constraints/ember.yaml
|
|
27
27
|
- examples/ember-app/enola/recipes/ember-conventions.yaml
|
|
28
|
+
- examples/knowledge-base/README.md
|
|
29
|
+
- examples/knowledge-base/enola-cluster.yaml
|
|
30
|
+
- examples/knowledge-base/wiki/_state/enola/findings.json
|
|
31
|
+
- examples/knowledge-base/wiki/_state/enola/receipts.json
|
|
32
|
+
- examples/knowledge-base/wiki/shop/permanent/architecture.md
|
|
33
|
+
- examples/knowledge-base/wiki/shop/permanent/public-surface.md
|
|
28
34
|
- examples/laws/README.md
|
|
29
35
|
- examples/laws/clean-rings/README.md
|
|
30
36
|
- examples/laws/clean-rings/enola/constraints/clean.yaml
|
|
@@ -103,6 +109,10 @@ files:
|
|
|
103
109
|
- guides/06-hooks-and-agents.md
|
|
104
110
|
- guides/07-providers.md
|
|
105
111
|
- guides/08-beyond-one-repo.md
|
|
112
|
+
- guides/09-a-knowledge-base-with-enola.md
|
|
113
|
+
- guides/10-hooks.md
|
|
114
|
+
- guides/11-the-skills-as-they-run.md
|
|
115
|
+
- guides/12-the-wrapper.md
|
|
106
116
|
- lib/enola-guides.rb
|
|
107
117
|
- recipes/README.md
|
|
108
118
|
- recipes/ember-conventions.yaml
|
|
@@ -114,9 +124,18 @@ files:
|
|
|
114
124
|
- templates/enola-check.sh
|
|
115
125
|
- templates/enola-cluster.yaml
|
|
116
126
|
- templates/enola-findings.rb
|
|
127
|
+
- templates/hooks/intent-first-gate.sh
|
|
128
|
+
- templates/hooks/record.sh
|
|
129
|
+
- templates/hooks/session-refresh.sh
|
|
130
|
+
- templates/hooks/stop-debt.sh
|
|
131
|
+
- templates/skills/enola-citation-hygiene/SKILL.md
|
|
132
|
+
- templates/skills/enola-deepdive/SKILL.md
|
|
133
|
+
- templates/skills/enola-drift/SKILL.md
|
|
117
134
|
- templates/skills/enola-grade-before-push/SKILL.md
|
|
135
|
+
- templates/skills/enola-ingest-check/SKILL.md
|
|
118
136
|
- templates/skills/enola-intent-first/SKILL.md
|
|
119
137
|
- templates/skills/enola-laws/SKILL.md
|
|
138
|
+
- templates/skills/enola-zoom-out/SKILL.md
|
|
120
139
|
- templates/skills/enola/SKILL.md
|
|
121
140
|
homepage: https://github.com/misabegovic/enola-guides
|
|
122
141
|
licenses:
|