Why (audit findings, from a prior session's step-1 pass)
The promote/pending flow (#38/#81) does not depend on knowledge/.brain_pending/brain_promote (ingestion/internal/api/promote.go) operate exclusively on brainDir/raw/ → brainDir/wiki/<wing>/<hall>/. They never touch brainDir/knowledge/. Removing the knowledge/ write path does not break #38/#81 — the issue's own caution flag is resolved.
No live caller uses domain/type/the legacy knowledge/ path. Checked hyperguild itself, mathias/skills (every skill using brain_write uses wing/hall only), mathias/agentsquad, mathias/dispatch, mathias/cad, mathias/cad-atlas — none found.
Bonus: dead orphaned code.internal/skills/brain/ (skill.go + handlers.go, repo-root module) defined a third, even-more-legacy brain_write shape (type/domain/filename, no wing/hall at all) for an old "supervisor" MCP server whose cmd/supervisor/main.go no longer exists. Confirmed nothing outside its own test file imported it — deleted entirely, since it encodes the exact legacy shape this issue removes, just in a dead code path.
What changed
ingestion/internal/mcp/handlers.go — brain_write's tool schema and brainWriteArgs drop type/domain; wing+hall are now required in the schema.
ingestion/internal/api/handler.go — WriteNote has one behaviour: wing+hall required, routes to wiki/<wing>/<hall>/. writeLegacyNote and the knowledge/-path branch are deleted.
ingestion/internal/capture/ports.go, ingestion/internal/brainstore/store.go — drop Type/Domain from capture.Note and its WriteNoteOptions construction. pipeline/*.go's domain field (used by brain_ingest_raw) is a different, legitimate concept — untouched.
internal/skills/brain/ — deleted (orphaned, dead since cmd/supervisor was removed).
Judgment call: what happens when wing/hall is omitted
With the knowledge/ fallback gone, there's no other valid destination for a write, so WriteNote now returns a flat "wing and hall are required" error whenever either is missing (previously: mixed messages — "wing and hall must be set together" only when exactly one was set, silent legacy fallback when neither was set). This surfaces as a 400 over HTTP and a tool-call error over MCP. I judged this the sane, unsurprising failure mode: one required-fields error, no special-cased partial state, no silent write to a location the caller didn't ask for.
A caller still sending stale domain/type args (there are none live, per the audit) does not get an error — the fields no longer exist on the arg struct, so json.Unmarshal silently drops them, and the write proceeds as a normal wing+hall write. Pinned by TestBrainWriteIgnoresLegacyTypeAndDomainParams.
Tests
TDD: added the new required-wing/hall test first, watched it fail for the right reason (RequiresWingAndHall/RequiresError on the old two-path split), then implemented.
Removed (tested behaviour that's intentionally gone): TestWrite_CreatesKnowledgeFile (rewritten as TestWrite_CreatesWikiFile), TestWrite_IncludesFrontmatterWhenTypeProvided, TestWriteNote_HallRouteExistingTypeWinsOverOptsType, TestBrainWriteRejectsTraversal, TestBrainWriteAcceptsDoubleDotInName — the latter two tested the deleted legacy path's own filename-validation; the underlying traversal-safety property (via brain.Sanitise) on the surviving wing/hall route is independently covered by internal/brain/path_test.go's TestNotePath_SanitisesWingAndSlug / TestSanitise, unaffected by this change.
Verification
task check (both Go modules: test -race, vet, govulncheck, build, gitleaks) exits 0, zero FAIL.
Risk/medium (removes public MCP tool-schema surface) — not merging; awaiting independent adversarial review per the sprint's review discipline.
Closes #79.
## Why (audit findings, from a prior session's step-1 pass)
1. **The promote/pending flow (#38/#81) does not depend on `knowledge/`.** `brain_pending`/`brain_promote` (`ingestion/internal/api/promote.go`) operate exclusively on `brainDir/raw/` → `brainDir/wiki/<wing>/<hall>/`. They never touch `brainDir/knowledge/`. Removing the `knowledge/` write path does not break #38/#81 — the issue's own caution flag is resolved.
2. **No live caller uses `domain`/`type`/the legacy `knowledge/` path.** Checked hyperguild itself, `mathias/skills` (every skill using `brain_write` uses `wing`/`hall` only), `mathias/agentsquad`, `mathias/dispatch`, `mathias/cad`, `mathias/cad-atlas` — none found.
3. **Bonus: dead orphaned code.** `internal/skills/brain/` (`skill.go` + `handlers.go`, repo-root module) defined a third, even-more-legacy `brain_write` shape (`type`/`domain`/`filename`, no wing/hall at all) for an old "supervisor" MCP server whose `cmd/supervisor/main.go` no longer exists. Confirmed nothing outside its own test file imported it — deleted entirely, since it encodes the exact legacy shape this issue removes, just in a dead code path.
## What changed
- `ingestion/internal/mcp/handlers.go` — `brain_write`'s tool schema and `brainWriteArgs` drop `type`/`domain`; `wing`+`hall` are now `required` in the schema.
- `ingestion/internal/api/handler.go` — `WriteNote` has **one** behaviour: wing+hall required, routes to `wiki/<wing>/<hall>/`. `writeLegacyNote` and the `knowledge/`-path branch are deleted.
- `ingestion/internal/capture/ports.go`, `ingestion/internal/brainstore/store.go` — drop `Type`/`Domain` from `capture.Note` and its `WriteNoteOptions` construction. `pipeline/*.go`'s `domain` field (used by `brain_ingest_raw`) is a different, legitimate concept — untouched.
- `internal/skills/brain/` — deleted (orphaned, dead since `cmd/supervisor` was removed).
## Judgment call: what happens when wing/hall is omitted
With the `knowledge/` fallback gone, there's no other valid destination for a write, so `WriteNote` now returns a flat `"wing and hall are required"` error whenever either is missing (previously: mixed messages — `"wing and hall must be set together"` only when exactly one was set, silent legacy fallback when neither was set). This surfaces as a 400 over HTTP and a tool-call error over MCP. I judged this the sane, unsurprising failure mode: one required-fields error, no special-cased partial state, no silent write to a location the caller didn't ask for.
A caller still sending stale `domain`/`type` args (there are none live, per the audit) does **not** get an error — the fields no longer exist on the arg struct, so `json.Unmarshal` silently drops them, and the write proceeds as a normal wing+hall write. Pinned by `TestBrainWriteIgnoresLegacyTypeAndDomainParams`.
## Tests
TDD: added the new required-wing/hall test first, watched it fail for the right reason (`RequiresWingAndHall`/`RequiresError` on the old two-path split), then implemented.
- Added: `TestWriteNote_RequiresWingAndHall` (api), `TestBrainWriteRequiresWingAndHall` + `TestBrainWriteIgnoresLegacyTypeAndDomainParams` (mcp), `TestWrite_RequiresWingAndHall` (HTTP-contract counterpart).
- Removed (tested behaviour that's intentionally gone): `TestWrite_CreatesKnowledgeFile` (rewritten as `TestWrite_CreatesWikiFile`), `TestWrite_IncludesFrontmatterWhenTypeProvided`, `TestWriteNote_HallRouteExistingTypeWinsOverOptsType`, `TestBrainWriteRejectsTraversal`, `TestBrainWriteAcceptsDoubleDotInName` — the latter two tested the deleted legacy path's own filename-validation; the underlying traversal-safety property (via `brain.Sanitise`) on the surviving wing/hall route is independently covered by `internal/brain/path_test.go`'s `TestNotePath_SanitisesWingAndSlug` / `TestSanitise`, unaffected by this change.
## Verification
`task check` (both Go modules: test -race, vet, govulncheck, build, gitleaks) exits 0, zero FAIL.
Risk/medium (removes public MCP tool-schema surface) — not merging; awaiting independent adversarial review per the sprint's review discipline.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
_Created via git-mcp on behalf of @mathias_
hyperguild#79's caller audit (done in a prior session) found the removal
was safe:
- The promote/pending flow (#38/#81, ingestion/internal/api/promote.go)
operates exclusively on brainDir/raw/ -> brainDir/wiki/<wing>/<hall>/. It
never touches brainDir/knowledge/, so removing the knowledge/ write path
does not break it.
- No live caller uses domain/type or the knowledge/ path: checked
hyperguild itself, mathias/skills, mathias/agentsquad, mathias/dispatch,
mathias/cad, mathias/cad-atlas.
- internal/skills/brain (a skill.go + handlers.go pair) defined a third,
even-more-legacy brain_write shape (type/domain/filename, no wing/hall)
for an old "supervisor" MCP server whose cmd/supervisor/main.go no
longer exists. Nothing outside its own test imported it; deleted
entirely as it encodes the exact legacy shape this issue removes.
Changes:
- ingestion/internal/mcp/handlers.go: brain_write's tool schema and
brainWriteArgs drop type/domain; wing+hall are now marked required in
the schema.
- ingestion/internal/api/handler.go: WriteNote has one behaviour — wing+
hall required, routing to wiki/<wing>/<hall>/. writeLegacyNote and the
knowledge/-path branch are deleted. Omitting wing or hall now returns
"wing and hall are required" (400 over HTTP, a tool error over MCP) —
the only sane failure mode left once there is no other valid
destination.
- ingestion/internal/capture/ports.go, internal/brainstore/store.go: drop
Type/Domain from capture.Note and its WriteNoteOptions construction.
pipeline/*.go's unrelated "domain" field (a different, legitimate
concept for brain_ingest_raw) is untouched.
- internal/skills/brain/: deleted (orphaned legacy tool schema, dead
since cmd/supervisor was removed).
Tests: added TestWriteNote_RequiresWingAndHall (api),
TestBrainWriteRequiresWingAndHall +
TestBrainWriteIgnoresLegacyTypeAndDomainParams (mcp) for the new single
behaviour; removed tests that only exercised the deleted legacy path
(TestWrite_IncludesFrontmatterWhenTypeProvided,
TestWriteNote_HallRouteExistingTypeWinsOverOptsType,
TestBrainWriteRejectsTraversal, TestBrainWriteAcceptsDoubleDotInName —
the latter two's filename-sanitisation behaviour is covered independently
by internal/brain/path_test.go's TestNotePath_SanitisesWingAndSlug /
TestSanitise, which is unaffected by this change).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PR #131 removed WriteNote's legacy brain/knowledge/ fallback branch that
activated when wing/hall were both empty, but never updated the CLI that
was the only caller relying on it. Since `hyperguild brain write` never
sent wing/hall, every invocation of the CLI's most load-bearing brain
command started hard-erroring "wing and hall are required" — a live,
README-documented command broken on every call.
Separately, writeRequest never had a `slug` field (pre-existing, unrelated
to #131): the CLI's slug value was silently dropped server-side even
before this PR, falling back to a timestamp filename.
Fix:
- `hyperguild brain write <wing> <hall> <slug>` replaces the old
`<type> <slug>` shape. Positional args, mirroring the existing
`brain query <topic>` pattern and matching writeHallNote's own
wing/hall/slug parameter order server-side (handler.go's
brain.NotePath(brainDir, wing, hall, slug)) — no new flags needed since
wing/hall/slug are all required, not optional filters.
- brainClient.Write now sends {wing, hall, filename, content}, matching
writeRequest exactly (writeRequest already gained a Filename field in
#131, so no server-side change was needed — only the CLI was sending
the wrong shape). This also fixes the dead slug bug: slug now arrives
server-side as filename instead of being silently dropped.
- Added TestWrite_CLIPayloadRoundTrips in ingestion/internal/api, which
POSTs the CLI's exact JSON envelope to the real Handler.Write (not a
mock) and asserts a real file with real wing/hall frontmatter lands on
disk. cmd/hyperguild and ingestion are separate Go modules (no
go.work), so this can't be a single cross-module test — instead the
wire contract is pinned from the server side, closing the gap where
cmd/hyperguild/http_test.go's TestBrainClient_Write_Success used only a
hand-rolled httptest mock and stayed green through the wing/hall
regression.
- Updated cmd/hyperguild/README.md and the 2026-05-03 CLI design spec's
stale `<type> <slug>` examples.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixed the regression found in review (commit 975b61c, pushed on top of this PR's branch).
Root cause (recap):WriteNote's legacy brain/knowledge/ fallback (activated when wing/hall were both empty) was removed by this PR, but hyperguild brain write — the only real caller — never sent wing/hall. Every invocation of the CLI's write command started hard-erroring "wing and hall are required". Separately (pre-existing, unrelated to this PR): writeRequest never had a slug field, so the CLI's slug was always silently dropped server-side.
Fix:
New CLI shape: hyperguild brain write <wing> <hall> <slug> (was <type> <slug>). Chose three positional args over flags because wing/hall/slug are all required (not optional filters like query's wing/hall), and this mirrors writeHallNote's own parameter order server-side (brain.NotePath(brainDir, wing, hall, slug)).
brainClient.Write now sends {wing, hall, filename, content}, matching writeRequest exactly — writeRequest already had a Filename field from this PR, so no server-side change was needed, only the CLI's payload. This also fixes the dead-slug bug: slug now arrives as filename instead of being dropped.
Closed the test gap the reviewer flagged: added TestWrite_CLIPayloadRoundTrips in ingestion/internal/api/handler_test.go, which POSTs the CLI's exact JSON envelope to the realHandler.Write (not a mock) and asserts a real file with real wing/hall frontmatter lands on disk under a temp brainDir. cmd/hyperguild and ingestion are separate Go modules (no go.work), so this can't be one cross-module test — the wire contract is pinned from the server side instead, which is what actually catches this class of break (the old cmd/hyperguild/http_test.go mock couldn't).
Updated cmd/hyperguild/README.md and the 2026-05-03 CLI design spec's stale <type> <slug> examples.
task check (test+vet+govulncheck+build+gitleaks across both modules) is green, exit 0, zero FAIL, verified directly (not grep-filtered).
Fixed the regression found in review (commit 975b61c, pushed on top of this PR's branch).
**Root cause (recap):** `WriteNote`'s legacy `brain/knowledge/` fallback (activated when wing/hall were both empty) was removed by this PR, but `hyperguild brain write` — the only real caller — never sent wing/hall. Every invocation of the CLI's write command started hard-erroring `"wing and hall are required"`. Separately (pre-existing, unrelated to this PR): `writeRequest` never had a `slug` field, so the CLI's slug was always silently dropped server-side.
**Fix:**
- New CLI shape: `hyperguild brain write <wing> <hall> <slug>` (was `<type> <slug>`). Chose three positional args over flags because wing/hall/slug are all required (not optional filters like `query`'s wing/hall), and this mirrors `writeHallNote`'s own parameter order server-side (`brain.NotePath(brainDir, wing, hall, slug)`).
- `brainClient.Write` now sends `{wing, hall, filename, content}`, matching `writeRequest` exactly — `writeRequest` already had a `Filename` field from this PR, so no server-side change was needed, only the CLI's payload. This also fixes the dead-slug bug: slug now arrives as `filename` instead of being dropped.
- Closed the test gap the reviewer flagged: added `TestWrite_CLIPayloadRoundTrips` in `ingestion/internal/api/handler_test.go`, which POSTs the CLI's exact JSON envelope to the **real** `Handler.Write` (not a mock) and asserts a real file with real wing/hall frontmatter lands on disk under a temp brainDir. `cmd/hyperguild` and `ingestion` are separate Go modules (no `go.work`), so this can't be one cross-module test — the wire contract is pinned from the server side instead, which is what actually catches this class of break (the old `cmd/hyperguild/http_test.go` mock couldn't).
- Updated `cmd/hyperguild/README.md` and the 2026-05-03 CLI design spec's stale `<type> <slug>` examples.
`task check` (test+vet+govulncheck+build+gitleaks across both modules) is green, exit 0, zero FAIL, verified directly (not grep-filtered).
Not merging — leaving that to you.
---
_Created via git-mcp on behalf of @mathias_
mathias
merged commit f324f04944 into main2026-09-23 05:43:28 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #79.
Why (audit findings, from a prior session's step-1 pass)
knowledge/.brain_pending/brain_promote(ingestion/internal/api/promote.go) operate exclusively onbrainDir/raw/→brainDir/wiki/<wing>/<hall>/. They never touchbrainDir/knowledge/. Removing theknowledge/write path does not break #38/#81 — the issue's own caution flag is resolved.domain/type/the legacyknowledge/path. Checked hyperguild itself,mathias/skills(every skill usingbrain_writeuseswing/hallonly),mathias/agentsquad,mathias/dispatch,mathias/cad,mathias/cad-atlas— none found.internal/skills/brain/(skill.go+handlers.go, repo-root module) defined a third, even-more-legacybrain_writeshape (type/domain/filename, no wing/hall at all) for an old "supervisor" MCP server whosecmd/supervisor/main.gono longer exists. Confirmed nothing outside its own test file imported it — deleted entirely, since it encodes the exact legacy shape this issue removes, just in a dead code path.What changed
ingestion/internal/mcp/handlers.go—brain_write's tool schema andbrainWriteArgsdroptype/domain;wing+hallare nowrequiredin the schema.ingestion/internal/api/handler.go—WriteNotehas one behaviour: wing+hall required, routes towiki/<wing>/<hall>/.writeLegacyNoteand theknowledge/-path branch are deleted.ingestion/internal/capture/ports.go,ingestion/internal/brainstore/store.go— dropType/Domainfromcapture.Noteand itsWriteNoteOptionsconstruction.pipeline/*.go'sdomainfield (used bybrain_ingest_raw) is a different, legitimate concept — untouched.internal/skills/brain/— deleted (orphaned, dead sincecmd/supervisorwas removed).Judgment call: what happens when wing/hall is omitted
With the
knowledge/fallback gone, there's no other valid destination for a write, soWriteNotenow returns a flat"wing and hall are required"error whenever either is missing (previously: mixed messages —"wing and hall must be set together"only when exactly one was set, silent legacy fallback when neither was set). This surfaces as a 400 over HTTP and a tool-call error over MCP. I judged this the sane, unsurprising failure mode: one required-fields error, no special-cased partial state, no silent write to a location the caller didn't ask for.A caller still sending stale
domain/typeargs (there are none live, per the audit) does not get an error — the fields no longer exist on the arg struct, sojson.Unmarshalsilently drops them, and the write proceeds as a normal wing+hall write. Pinned byTestBrainWriteIgnoresLegacyTypeAndDomainParams.Tests
TDD: added the new required-wing/hall test first, watched it fail for the right reason (
RequiresWingAndHall/RequiresErroron the old two-path split), then implemented.TestWriteNote_RequiresWingAndHall(api),TestBrainWriteRequiresWingAndHall+TestBrainWriteIgnoresLegacyTypeAndDomainParams(mcp),TestWrite_RequiresWingAndHall(HTTP-contract counterpart).TestWrite_CreatesKnowledgeFile(rewritten asTestWrite_CreatesWikiFile),TestWrite_IncludesFrontmatterWhenTypeProvided,TestWriteNote_HallRouteExistingTypeWinsOverOptsType,TestBrainWriteRejectsTraversal,TestBrainWriteAcceptsDoubleDotInName— the latter two tested the deleted legacy path's own filename-validation; the underlying traversal-safety property (viabrain.Sanitise) on the surviving wing/hall route is independently covered byinternal/brain/path_test.go'sTestNotePath_SanitisesWingAndSlug/TestSanitise, unaffected by this change.Verification
task check(both Go modules: test -race, vet, govulncheck, build, gitleaks) exits 0, zero FAIL.Risk/medium (removes public MCP tool-schema surface) — not merging; awaiting independent adversarial review per the sprint's review discipline.
🤖 Generated with Claude Code
Created via git-mcp on behalf of @mathias
Fixed the regression found in review (commit
975b61c, pushed on top of this PR's branch).Root cause (recap):
WriteNote's legacybrain/knowledge/fallback (activated when wing/hall were both empty) was removed by this PR, buthyperguild brain write— the only real caller — never sent wing/hall. Every invocation of the CLI's write command started hard-erroring"wing and hall are required". Separately (pre-existing, unrelated to this PR):writeRequestnever had aslugfield, so the CLI's slug was always silently dropped server-side.Fix:
hyperguild brain write <wing> <hall> <slug>(was<type> <slug>). Chose three positional args over flags because wing/hall/slug are all required (not optional filters likequery's wing/hall), and this mirrorswriteHallNote's own parameter order server-side (brain.NotePath(brainDir, wing, hall, slug)).brainClient.Writenow sends{wing, hall, filename, content}, matchingwriteRequestexactly —writeRequestalready had aFilenamefield from this PR, so no server-side change was needed, only the CLI's payload. This also fixes the dead-slug bug: slug now arrives asfilenameinstead of being dropped.TestWrite_CLIPayloadRoundTripsiningestion/internal/api/handler_test.go, which POSTs the CLI's exact JSON envelope to the realHandler.Write(not a mock) and asserts a real file with real wing/hall frontmatter lands on disk under a temp brainDir.cmd/hyperguildandingestionare separate Go modules (nogo.work), so this can't be one cross-module test — the wire contract is pinned from the server side instead, which is what actually catches this class of break (the oldcmd/hyperguild/http_test.gomock couldn't).cmd/hyperguild/README.mdand the 2026-05-03 CLI design spec's stale<type> <slug>examples.task check(test+vet+govulncheck+build+gitleaks across both modules) is green, exit 0, zero FAIL, verified directly (not grep-filtered).Not merging — leaving that to you.
Created via git-mcp on behalf of @mathias