Chemenu 2.1.0 - deterministischer Wissenskompiler
Chemenu kompiliert Rohnotizen zu einem verlinkten, quellengebundenen Wiki: raw/ -> types/ + tools/ -> kb/ -> reports/. Was mechanisch ist, macht tools/wikitool; was Urteil braucht, macht ein Agent unter Contracts, deren Grenzen in Code durchgesetzt sind statt im Prompt. Dieser Commit ist der Startpunkt der oeffentlichen Historie. Die vorherige Entwicklung fand in einer privaten Instanz statt und ist nicht Teil dieses Repositorys; ihre Erzaehlung steht vollstaendig in CHANGES.md, das mit 44 Eintraegen von 0.1.0 bis 2.1.0 erhalten geblieben ist. Der mitgelieferte Korpus ist ein Testbett und eine Demo: 170 Seiten ueber den Stack selbst - Gates, Lint, Versionierung, Suche, das Wiki-Muster. Er dokumentiert das Werkzeug mit den eigenen Mitteln des Werkzeugs. Lizenz: AGPL-3.0 fuer den Stack (tools/, types/), CC-BY-4.0 fuer die Inhalte. Die Grenze zwischen beiden ist der Dateiplan, den dist export berechnet - siehe NOTICE.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: commonplace-kb
|
||||
description: Vendored knowledge base on agent context engineering, memory and deploy-time learning - consult it before a design decision in those areas while developing this stack.
|
||||
---
|
||||
|
||||
# Consult the vendored commonplace/ knowledge base
|
||||
|
||||
`commonplace/kb/` is a vendored knowledge base on agent context engineering, memory, and
|
||||
deploy-time learning. It exists only in this dev instance - a distributed instance never
|
||||
carries it (see [tools/CONTRACT.md](../../tools/CONTRACT.md) for what `dist export` excludes).
|
||||
|
||||
## When to run
|
||||
|
||||
- Before a design decision in this repo's own instruction/skill/context layer - not for wiki
|
||||
*content* questions, which stay inside `kb/`.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Start at `commonplace/kb/notes/tags-README.md`.
|
||||
2. Paths named inside `commonplace/kb/` are relative to `commonplace/`, not to this repo's root.
|
||||
3. It is read-only in this project. To contest a claim, open an issue at
|
||||
https://github.com/zby/commonplace/issues - never edit it here.
|
||||
|
||||
## Scope
|
||||
|
||||
Only relevant while working in [stack-dev](stack-dev/SKILL.md) mode. Not part of the wiki
|
||||
content pipeline, and not linked from anything outside `instructions/dev/`.
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: issue-tracking
|
||||
description: Where open work on this stack is tracked, and what the prio/ and size/ labels on a Gitea issue mean.
|
||||
---
|
||||
# Track open work as Gitea issues, not as prose in the repo
|
||||
|
||||
Open work on this stack lives at
|
||||
<https://gitea.nehmer.net/torben/chemenu/issues>, one issue per work
|
||||
package, and nowhere else. There is no `TODO.md`; there was, and every item in
|
||||
it either became an issue or was already one, described twice.
|
||||
|
||||
That is the whole reason for this file: a second list is a second thing to
|
||||
maintain, and the one that drifts is always the one nobody reads first. The
|
||||
issue tracker wins that comparison outright - it has state, comments, labels,
|
||||
and a link that survives the change it describes. A markdown file in the repo
|
||||
has none of it, and it costs a publish to touch.
|
||||
|
||||
This instruction exists only in the dev repo. A distributed instance has no
|
||||
issues at that URL, which is exactly why `dist export` excludes
|
||||
`instructions/dev/` wholesale (see [tools/CONTRACT.md](../../tools/CONTRACT.md)).
|
||||
|
||||
## When to run
|
||||
|
||||
- Something is worth doing but not now. Open an issue; do not write it down in
|
||||
the repo.
|
||||
- A session's findings outgrow the change it was making - a gap in the tooling,
|
||||
an assumption nobody has checked, a decision that needs the user.
|
||||
- Prioritising: deciding what to pick up next, or re-labelling after the ground
|
||||
moved.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Write the issue so it survives without you.** What is broken or missing,
|
||||
why it matters, what "done" looks like as acceptance criteria, and the
|
||||
specific files or commands involved. An issue that only makes sense to
|
||||
whoever wrote it is a note, and notes were the problem.
|
||||
|
||||
2. **Give it exactly two labels: one `prio/`, one `size/`.** Both, always -
|
||||
a priority without a cost is half a decision. Neither is a promise about
|
||||
*when*; together they answer "what should I pick up in the time I have".
|
||||
|
||||
| Priority | Means |
|
||||
|---|---|
|
||||
| `prio/1` | Blocks or damages work in progress. Next. |
|
||||
| `prio/2` | Accrues interest. Planned. |
|
||||
| `prio/3` | Worth doing, waiting on a trigger. |
|
||||
|
||||
`prio/3` is not a graveyard. It means the issue's value is real but gated on
|
||||
something outside it - a decision, another issue, a second instance
|
||||
existing. Name that trigger in the issue, or the label is a polite no.
|
||||
|
||||
| Size | Means |
|
||||
|---|---|
|
||||
| `size/XS` | Minutes. Often just a decision or an observation to record. |
|
||||
| `size/S` | One session, one publish, a clear cut. |
|
||||
| `size/M` | Several files; a contract or instruction change; its own test effort. |
|
||||
| `size/L` | Several sessions, or open design questions before the first commit. |
|
||||
|
||||
Size is effort, not importance. A `prio/1 size/XS` is the best thing on the
|
||||
board; a `prio/3 size/L` is a thing to talk about before anyone starts.
|
||||
|
||||
3. **Re-label when the ground moves, and say why in a comment.** A trigger that
|
||||
fired turns `prio/3` into `prio/2`. A design question that got answered can
|
||||
drop a size. Silent re-labelling is how a board stops meaning anything.
|
||||
|
||||
4. **Close with what actually happened**, not with a commit hash alone: which
|
||||
proposals were implemented, which were deliberately left out and why, and
|
||||
what was verified. The issue is the only place that record survives - a
|
||||
changelog entry says what changed, not what was decided against.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **Issue or changelog?** An issue is work that is *not done*. `CHANGES.md` is
|
||||
what shipped. A finished change needs both: the entry, and the issue closed
|
||||
with the reasoning.
|
||||
- **Issue or `kb/` page?** An issue is about *this stack* and is ephemeral - it
|
||||
closes. A `kb/` page is compiled knowledge that stays true. Never put wiki
|
||||
content findings in an issue, and never file a work item as a page.
|
||||
- **Two labels feel too coarse?** They are meant to. A third axis - kind, area,
|
||||
status - is the point at which a taxonomy starts needing maintenance of its
|
||||
own, and this board has one maintainer.
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: stack-dev
|
||||
description: Switch a session into tool-development mode - extending tools/wikitool, the compiler, the type schema, or the instruction/skill layer itself, instead of operating on wiki content. Use when the user asks to add a wikitool command, change a type-spec, fix or extend the compiler, or otherwise work on the stack rather than ingest/query/manage/lint the wiki.
|
||||
---
|
||||
|
||||
# Stack Development Mode
|
||||
|
||||
**Purpose:** Recognize a session that is about the tool stack itself - `tools/wikitool`, the
|
||||
type schema, the instruction/skill layer - rather than wiki content, and switch the rules that
|
||||
apply accordingly.
|
||||
|
||||
**Trigger:** The user asks to add or change a `wikitool` command, extend the compiler, change a
|
||||
type-spec, or work on `instructions/`/`types/`/`tools/` as code rather than as a place to run
|
||||
`wiki-ingest`/`wiki-query`/`wiki-manage`/`wiki-lint`/`wiki-status` against.
|
||||
|
||||
**This directory is dev-only.** `instructions/dev/` is excluded wholesale by
|
||||
`tools/wikitool dist export` - nothing here ever reaches a distributed instance, and there is
|
||||
no restore path. If you are in a distributed instance, this skill should not be present at all;
|
||||
stack development happens in the origin repo instead (see AGENTS.md's routing line).
|
||||
|
||||
## What changes in this mode
|
||||
|
||||
- **Source-binding does not apply to code.** AGENTS.md invariant 3 ("never file an unsourced
|
||||
answer into the wiki") governs `kb/` content, not the code you write to extend the stack.
|
||||
Ordinary software-engineering judgment applies to `tools/chemenu/*.py`, `types/*`,
|
||||
`instructions/*` - it does not need a `raw/` source or a citation.
|
||||
- **Test and review conventions from `instructions/dev/` apply instead**, once written down
|
||||
there (step 2 below lists what currently exists). Until a given convention has its own
|
||||
instruction file, follow the existing test files' own patterns
|
||||
(`tools/chemenu/tests/`) rather than inventing a new one silently.
|
||||
- **Everything outside this directory still applies.** The tool error contract, the gates, and
|
||||
"never hand-edit generated files" (AGENTS.md invariants 1, 5-8) are about how the tool
|
||||
behaves at runtime, not about developing it, but they still bind normal session conduct
|
||||
(e.g. still use `tools/wikitool publish`, still respect the gates, when the session also
|
||||
touches wiki content).
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Confirm the mode.** If the task is ambiguous between "extend the tool" and "operate the
|
||||
wiki", ask rather than guess - the two have different rules for the same directories.
|
||||
2. **Consult `instructions/dev/` for the concrete procedure.** Currently:
|
||||
[commonplace-kb.md](../commonplace-kb.md) - vendored knowledge base on agent context
|
||||
engineering, memory and deploy-time learning; consult before a design decision in those
|
||||
areas.
|
||||
[issue-tracking.md](../issue-tracking.md) - open work lives in Gitea issues, one per work
|
||||
package, labelled `prio/1..3` and `size/XS..L`. There is no `TODO.md`. Read it before
|
||||
filing something for later, or before deciding what to pick up next.
|
||||
[testing-conventions.md](../testing-conventions.md) - the suite runs against a deliberately
|
||||
empty machine; what the autouse fixture already neutralizes, and what a test still has to
|
||||
establish itself. Read it before adding or changing a test.
|
||||
More instructions are added here incrementally as stack-development needs come up - this
|
||||
list grows without needing this skill file to change shape.
|
||||
3. **Raise the version, if the change ships.** A change under `tools/`, `types/`,
|
||||
`instructions/`, `AGENTS.md` or a `CONTRACT.md` reaches every future instance, so it needs a
|
||||
version and a changelog entry:
|
||||
|
||||
```bash
|
||||
tools/wikitool version bump --patch --title "<what changed>"
|
||||
```
|
||||
|
||||
Never edit `VERSION` or the entry's heading by hand - `bump` writes both, and `docs verify`
|
||||
fails a tree where they disagree. Pick the part by what an existing instance would have to do:
|
||||
|
||||
| Change | Part |
|
||||
|--------|------|
|
||||
| Fix, no interface change | `--patch` |
|
||||
| New capability, backwards compatible | `--minor` |
|
||||
| **Existing content must be migrated** | `--major` |
|
||||
|
||||
A `--major` bump additionally needs a migration document for the new version - written per
|
||||
[migrate-corpus.md](../../migrate-corpus.md) - or `--no-migration "<reason>"` when no content
|
||||
actually has to change. `bump` refuses otherwise, and so does `docs verify`: an instance
|
||||
learning that it must migrate, with nothing telling it how, is a dead end.
|
||||
|
||||
Then write the entry's body - `bump` deliberately leaves it empty, the same way `new` leaves
|
||||
the prose.
|
||||
|
||||
Prose-only changes (`README.md`, `INSTALL.md`, `EVALS.md`) and the workflows under `.gitea/`
|
||||
do not need a bump - CI's version gate is scoped to what changes behaviour.
|
||||
|
||||
4. **Verify before publishing.** `tools/wikitool docs verify`, `tools/wikitool instructions
|
||||
verify`, and the relevant `pytest` run in `tools/` - the same checks any stack change must
|
||||
pass, run explicitly rather than assumed. CI (`.gitea/workflows/ci.yml`) runs these plus a
|
||||
full `setup-instance.md` replay against a fresh `dist export`; a push to `main` that moves
|
||||
`VERSION` additionally triggers a tagged release. **CI does the tagging** - a session never
|
||||
creates a tag, which is what keeps AGENTS.md invariant 5 intact.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **Touches both stack code and wiki content in one session?** Apply this skill's rules to the
|
||||
code changes and the normal content skills' rules to the content changes - they are not
|
||||
mutually exclusive within a session, only per change.
|
||||
|
||||
## Scope
|
||||
|
||||
Not for wiki content work - use `wiki-ingest`/`wiki-query`/`wiki-manage`/`wiki-lint`/
|
||||
`wiki-status` for that. Not for setting up a new instance (`instructions/setup-instance.md`) or
|
||||
a fresh clone of this repo (`instructions/bootstrap.md`).
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: testing-conventions
|
||||
description: How to write a test for this stack so it passes on a machine that is not yours - what the hermetic environment fixture already handles, and what a test still has to establish itself.
|
||||
---
|
||||
# Write tests that do not depend on the machine they run on
|
||||
|
||||
Every test in `tools/chemenu/tests/` runs against a deliberately empty machine. That is not
|
||||
a convention you have to remember: the autouse `hermetic_environment` fixture in
|
||||
`tools/chemenu/tests/conftest.py` enforces it before each test, and
|
||||
`test_hermetic_env.py` asserts that the fixture still does. What you have to remember is the
|
||||
consequence - **a test that needs an identity, a token, or a home directory establishes it
|
||||
itself.**
|
||||
|
||||
This exists because the suite once did not. `config.default_author()` shells out to
|
||||
`git config user.name`, and for months the answer came from the global git configuration of
|
||||
whoever ran pytest. 628 tests were green on every developer machine and two of them failed on
|
||||
the first CI run that ever reached pytest, in a container that had no such configuration
|
||||
(Gitea #8). Two more tests of the same kind were written afterwards, by someone who had read
|
||||
that issue first - which is the argument for a fixture rather than a rule.
|
||||
|
||||
## What the fixture already neutralizes
|
||||
|
||||
Do not re-do any of this per test; it is done for you, per test, via `monkeypatch`.
|
||||
|
||||
| Neutralized | To |
|
||||
|---|---|
|
||||
| `HOME` | a fresh empty directory in that test's `tmp_path` (also the fixture's return value) |
|
||||
| `XDG_CONFIG_HOME` | `$HOME/.config`, which does not exist |
|
||||
| `GIT_CONFIG_GLOBAL`, `GIT_CONFIG_SYSTEM` | `/dev/null` - git's own way to say "no such file" |
|
||||
| `GIT_DIR`, `GIT_WORK_TREE`, `GIT_AUTHOR_*`, `GIT_COMMITTER_*`, `EMAIL` | unset |
|
||||
| `WIKI_AUTHOR`, `WIKI_TRACE`, `WIKI_TRACE_CONTENT`, `WIKI_TRACE_MAX_CONTENT`, `WIKITOOL_SESSION_ID`, `WIKITOOL_UPDATE_URL`, `WIKITOOL_UPDATE_TOKEN` | unset |
|
||||
|
||||
`WIKI_TRACE_DIR` is the one variable that stays *set*: the separate `isolated_trace_dir`
|
||||
fixture redirects it into `tmp_path`. Tracing is never disabled suite-wide, because two
|
||||
telemetry tests assert that a trace gets written.
|
||||
|
||||
## When to run
|
||||
|
||||
Whenever you add or change a test under `tools/chemenu/tests/`.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Decide whether the test needs an author identity.** It does if it reaches
|
||||
`wikitool new source` (through the `CliRunner` or otherwise), `doctor`, `migrate`, or
|
||||
anything else that stamps a page. Under the fixture there is no ambient identity, so the
|
||||
call fails with `ERROR No author configured for this instance.` if you skip this.
|
||||
|
||||
2. **Establish it explicitly, one of two ways** - pick by what the test is actually about:
|
||||
|
||||
- The test is about *something else* and just needs a page to exist:
|
||||
|
||||
```python
|
||||
monkeypatch.setenv("WIKI_AUTHOR", "Fixture Author") # no ambient identity under the fixture
|
||||
```
|
||||
|
||||
- The test is about *authorship itself* - then make the fixture root a real repository with
|
||||
a local identity, and assert the concrete name:
|
||||
|
||||
```python
|
||||
subprocess.run(["git", "init", "-q", "-b", "main"], cwd=root, check=True)
|
||||
subprocess.run(["git", "config", "user.name", "Fixture Author"], cwd=root, check=True)
|
||||
```
|
||||
|
||||
`-b main` is not cosmetic: without a global configuration git prints an
|
||||
`init.defaultBranch` advisory that clutters the output of a test that is failing for an
|
||||
unrelated reason.
|
||||
|
||||
3. **Never set an identity in `conftest.py` for everyone.** A shared default would make
|
||||
`default_author()`'s fallback untestable - the branch that returns `None` only exists on a
|
||||
machine that knows nobody, and `test_hermetic_env.py` covers it precisely because the
|
||||
fixture creates that machine.
|
||||
|
||||
4. **Adding a new environment variable to the tool?** Add it to `_WIKITOOL_ENV` in
|
||||
`conftest.py` in the same change. A variable the tool reads and the fixture does not clear
|
||||
is the exact hole this whole file is about, reopened.
|
||||
|
||||
5. **Verify against an empty machine before publishing**, not only in your own shell:
|
||||
|
||||
```bash
|
||||
cd tools && env -i PATH="$PATH" HOME="$(mktemp -d)" \
|
||||
GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null \
|
||||
.venv/bin/python -m pytest -q
|
||||
```
|
||||
|
||||
With the fixture in place this must produce exactly the same result as a plain
|
||||
`.venv/bin/python -m pytest -q`. A difference between the two is a leak, and the leaking
|
||||
variable belongs in step 4's list.
|
||||
|
||||
6. **Check the coverage report when adding tests to close a gap**, rather than guessing which
|
||||
lines were uncovered:
|
||||
|
||||
```bash
|
||||
cd tools && .venv/bin/python -m pytest -q --cov # needs pytest-cov, CI-only
|
||||
```
|
||||
|
||||
Read it by module, not by total. A thin Typer wrapper sitting low is evidence that the logic
|
||||
was cut out from under it and tested there; the list worth acting on is the modules whose
|
||||
*logic* is uncovered. EVALS.md § "How much of the stack the suite reaches" names both, and
|
||||
the measured baseline. There is no threshold to satisfy - the suite is not graded on the
|
||||
number.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **A test genuinely needs the developer's real environment?** There is no such test, and a new
|
||||
one is a design problem rather than an exception: what it wants is a fixture that *builds*
|
||||
the state it needs inside `tmp_path`. Building it is also the only version CI can run.
|
||||
- **A test patches `config.default_author` directly** (as
|
||||
`test_new_source_fails_hard_without_any_author` does)? Keep the patch. It is not made
|
||||
redundant by the fixture - it pins the value under test regardless of what the environment
|
||||
would have resolved to, and it is what keeps that test about the CLI's error path rather than
|
||||
about the environment.
|
||||
|
||||
## Scope
|
||||
|
||||
Applies to `tools/chemenu/tests/` only. It says nothing about what to test - the test/review
|
||||
expectations for a stack change are the `stack-dev` skill's step 4 (`docs verify`,
|
||||
`instructions verify`, pytest). CI runs the suite once, unhardened, because the fixture makes a
|
||||
second hardened run redundant; see the note on the Tests step in `.gitea/workflows/ci.yml`.
|
||||
Reference in New Issue
Block a user