Skip to content

enhance: rebuild skills architecture#8

Merged
mroops0111 merged 30 commits into
masterfrom
enhance/skills-architecture
May 28, 2026
Merged

enhance: rebuild skills architecture#8
mroops0111 merged 30 commits into
masterfrom
enhance/skills-architecture

Conversation

@mroops0111
Copy link
Copy Markdown
Owner

@mroops0111 mroops0111 commented May 28, 2026

Summary

  • DDD ontology plugin ships its own skills (braid-extract / braid-clarify / braid-model) and a shared concept doc; core stays ontology-agnostic.
  • Knowledge graph nodes accept multi-paragraph markdown descriptions with bullets, tables, and mermaid diagrams; bilingual name format (e.g. 建立訂單 (CreateOrder)) is supported. Studio renders both live.
  • braid-generate-doc renders documentation for any container-shaped node type the active ontology declares (driven by renderHint), not just DDD bounded contexts.
  • Skills declare typed inputs in frontmatter (text / pick / multi-pick) backed by static lists or live workspace data (graph nodes, intent documents, clarify tickets). Studio renders the typed form; multi-pick runs the skill in parallel across selected items.
  • Skills call typed MCP tools exposed by the built-in braid-core gateway, powered by OpenAPI MCP Gateway, replacing raw HTTP calls.
  • Proposal review and Graph view gain edge selection with a dedicated detail panel; Table view reaches parity with Canvas for diff highlights and selection.

mroops0111 and others added 29 commits May 26, 2026 22:33
`braid.requiredPaths` lived in the skill frontmatter schema but had no
production consumer — only test fixtures referenced it. Removing it
trims dead surface area and keeps the frontmatter contract honest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codify the SKILL.md contract that the upcoming SkillStructureValidator
will enforce: required H2 sections per category, table-vs-bullet rule,
Companion-docs-only References convention, ontology-agnostic vs
ontology-specific skill placement, frontmatter contract.

Lives under packages/core/skills/ so it ships with the @braidhq/core
package alongside the contract it governs (the top-level docs/
directory is gitignored).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace product-specific identifiers (signing-domain task/signers/stage
vocabulary, apps/api/task paths, DottedSign-flavoured permission strings)
with neutral orders/checkout placeholders so the shared docs read as
generic guidance for any workspace.

api-routes.md left untouched in this commit; it will be deleted after
the MCP migration replaces curl-based skill flows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch health, ontology, model, nodes, edges, and decisions over to
@hono/zod-openapi 0.19. Each route now declares operationId / summary
/ tags / request params / response schemas via createRoute. The
top-level app is now OpenAPIHono and exposes /openapi.json so an
openapi-mcp-gateway sidecar can mirror these endpoints as MCP tools.

Write-side routers (proposals / clarify / skills) and out-of-spec
routers (workspaces / oauth / runs / workspaceEvents) keep their
existing shape and will be handled in follow-up commits.

routes/_shared.ts collects reusable OpenAPI building blocks
(WorkspaceIdParam, NotFoundResponse, ValidationFailureResponse) so
the workspace-scoped routes don't drift on parameter declarations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
proposals, clarify, and skills are now declared via createRoute so the
OpenAPI spec describes their request body / response shapes. The
clarify GET projection (skipReason / answerNote) is captured as an
extended response schema rather than freeform JSON.

The createProposal and createClarifyTicket operations are the primary
write-side tools an openapi-mcp-gateway sidecar will expose to skills;
their operationId / summary / description are written for LLM
consumption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a /openapi.json test that pins two things:
- All skill-facing read/write operations (search nodes, scope,
  submit proposal, submit clarify, …) appear with stable operationIds.
- SSE streams, the OAuth HTML callback, and workspace-management
  admin routes are intentionally absent — they aren't invocable as
  MCP tools so openapi-mcp-gateway shouldn't see them.

No code changes; the exclusion already comes from those routers
still using plain Hono mounts. This test pins the contract so a
later commit can't drift it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
buildMcpConfig now accepts extraServers. SubprocessSkillRunner takes a
new coreGatewayUrl option that, when set, injects a `braid-core`
Streamable HTTP entry into every spawned skill's mcp-config — pointing
at a running openapi-mcp-gateway that mirrors GET /openapi.json as
MCP tools. composeFsApp reads the URL from BRAID_MCP_GATEWAY_URL env.

Workspace-declared MCP entries with the id `braid-core` take
precedence so a workspace can override the built-in if needed.

Gateway is a Python package run separately; setup is documented in
packages/server/README.md. Without the env var the wiring is a no-op
(skills fall back to curl as before this PR).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure validator that takes a SKILL.md body + parsed frontmatter and
reports missing required H2 sections. Common required sections (Role,
Inputs & Outputs, Design Principles, Initialization, Procedure, Output,
Failure Handling, Completion Checklist, Companion docs) plus
category-specific (Output Files for generate).

Fenced code blocks are skipped so a `## Role` line embedded in an
example doesn't masquerade as a real section. H3 / inline anchors are
ignored.

Not wired into FsSkillRegistry yet; that switch lands after each
SKILL.md has been refactored to the new structure (see commit
chain). Until then this is just a library function with full unit
coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply the style guide's H2 section contract: Role / Inputs & Outputs /
Design Principles / Initialization / Procedure / Output / Failure
Handling / Completion Checklist / Companion docs.

Switch the procedure from curl + jq pipelines to typed MCP tool calls
on the `braid-core` server (`listNodes`, `getNodeScope`, `getOntology`).
Companion docs no longer indexes API surface — that lives in the MCP
tool contract; only the lazy-load supplementary docs (drift-detection.md)
stay.

Field-level references switched away from `node.refs.prd` /
`node.refs.implementedIn` (legacy) to `metadata.sourceReferences`,
matching the live graph schema.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply the style guide H2 contract. Switch curl + jq to MCP tool calls
(getClarifyTicket, listClarifyTickets, createProposal,
createClarifyTicket, markClarifyTicketApplied).

Sharpen the Step-2 decision rule — minor supplementary ops preserve
the reviewer's intent; major issues escalate to a new ClarifyTicket
rather than force-applying. Failure Handling lays out the
post-validation retry rules so partial failures don't lose work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply the style guide H2 contract plus the generate-category-only
'## Output Files' section. Switch curl + jq to MCP tool calls
(getOntology, listNodes, getNodeScope).

The Step-2 traversal logic still encodes DDD vocabulary (ctx → agg →
cmd/qry → evt/rule). Marked with a TODO pointing to the upcoming
renderHint-driven traversal that will let other ontologies reuse this
skill without rewriting.

De-specified container example from `signup` to `checkout` to keep
the file domain-neutral.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The procedure encodes DDD-specific structural rules (BoundedContext
contains aggregates only, the seven Context Mapping edges, Vernon
Process Manager). Move it out of @braidhq/core (which should be
ontology-agnostic at the prompt layer) into @braidhq/ontology-ddd
and declare it via the existing Plugin.skills seam.

PluginRegistry + FsSkillRegistry already implement plugin-origin
skill loading; no new registration mechanism needed.

While moving, apply the style guide H2 contract and switch curl + jq
to MCP tool calls (getOntology, getModelSnapshot, createProposal,
createClarifyTicket). Update package.json files: array so skills/
ships with the published artifact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same reasoning as braid-extract: braid-model's procedure is built
around DDD structural rules (containment, bridge edges between
aggregates, the seven Context Mapping patterns) and belongs alongside
the DDD ontology rather than in @braidhq/core.

Apply the style guide H2 contract (Modes section retained as a
build-category-specific extra). Switch curl + jq to MCP tool calls
(getOntology, getModelSnapshot, listNodes, createProposal,
createClarifyTicket). The legacy /model/validate endpoint is no
longer called directly — createProposal returns the same validation
issues inline, so a separate validateProposal round trip is
unnecessary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…fail)

FsSkillRegistry now runs validateSkillStructure on every SKILL.md it
reads (builtin / plugin / workspace). A skill missing a required H2
section throws on load with a message citing the file path and
missing sections — better to fail loudly at boot than to spawn a
half-broken skill that silently skips Procedure or Output.

The shipped SKILL.md files (braid-ask / braid-clarify /
braid-generate-doc / braid-extract / braid-model) all already
conform after the per-skill refactor commits earlier in this chain.
Also nudged a couple of `## Procedure (per ticket)` / `(per scope)`
headings back to plain `## Procedure` so the validator's exact-match
contract isn't tripped by parenthetical qualifiers.

Test fixtures that materialise minimal SKILL.md files were getting
rejected by the new validator; a `makeSkillFileContents` helper
emits a body that satisfies the contract so test writers don't have
to think about it. Added a regression test that an intentionally
malformed SKILL.md is rejected with a clear error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the MCP migration, every skill calls the server through typed
MCP tools (createProposal, listNodes, getOntology, …) rather than
hand-rolled curl pipelines. The endpoint reference doc was the only
reason the file existed; its replacement is the OpenAPI spec at
GET /openapi.json plus the gateway's tool listing.

Verified no SKILL.md or shared doc references it anymore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Optional hint set by an ontology to tell ontology-agnostic renderers
(notably braid-generate-doc) how its node types compose in a doc tree:

- container: marks a type as a top-level grouping (one file per node).
- expandedUnder: names the type whose nodes act as parents in the
  rendered tree — children of nodes of that type appear nested under
  them.
- section: human-facing heading text the renderer uses when grouping
  flat lists of this type.

All fields are optional. A descriptor with no hint is rendered as a
footnote / leaf. The `/ontology` API surfaces the field automatically
via the existing zod schema.

The DDD ontology starts using this in the next commit; the doc-render
skill consumes it in the commit after that.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire each DDD node type to the rendering taxonomy:

- boundedContext: container = true, section = 'Use cases'
- aggregate: expandedUnder = boundedContext
- command / query: expandedUnder = aggregate
- event / rule: expandedUnder = command
- actor: section = 'Actors' (flat list, not nested)
- policy: section = 'Reactions' (flat list)

This is the metadata that lets braid-generate-doc render a DDD graph
without the skill knowing DDD vocabulary; the renderer just walks
container → expandedUnder chains.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the hardcoded `ctx → agg → cmd/qry → evt/rule` traversal with
one that reads each `NodeTypeDescriptor.renderHint` from the
getOntology response:

- container types → one output file per node of that type.
- expandedUnder chains → nested rendering, deepest level becomes
  the leaf in the rendered document.
- section types → a flat H2 list at the container level.
- types without a renderHint → footnoted in the source-node list,
  not promoted into the body.

The procedure no longer names DDD concepts (aggregate / command /
event / rule). A non-DDD ontology that declares its own renderHint
chains gets rendered without changing this skill. Dropped the
TODO marker that pointed to this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e headings

This PR's earlier commits added two H2 sections to every SKILL.md
that weren't in the original structure:

- Inputs & Outputs: largely duplicated frontmatter (env / argument-hint
  / category). The Role section already names the key MCP tools when
  they're worth highlighting.
- Failure Handling: rarely had enough substance to merit its own
  section. The failure paths that matter live inside the Procedure
  step that produces them (e.g. createProposal validation rules in
  Step 4); generic "log it, continue with partial data" lines went
  into Notes.

Both were scope creep over what the brief actually asked for
(structural consistency, not more detail). Removed from the validator,
the style guide, the test fixture helper, the unit tests, and all
five shipped SKILL.md files.

While here:

- Rename `## Companion docs` → `## Companion Docs` (Title Case, per
  the heading rule the style guide now states explicitly).
- Drop the redundant `# braid-<id>` H1 from each SKILL.md — the
  frontmatter `name:` is already canonical.

Companion Docs entries forward-reference proposal-format.md /
clarify-format.md / validators.md, which arrive in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ors.md

Replace `shared/artifact-formats.md` (mixed Proposal + ClarifyTicket +
GraphOperation + DriftIssue + scratch-file notes in one file) with
per-artifact docs:

- `proposal-format.md`: Proposal request body, GraphOperation
  discriminated union, NewGraphNode / NewGraphEdge payload shapes,
  id-prefix conventions, status semantics, ≤ 30 ops/proposal sizing.
  Forward-references drift-detection.md for the DriftIssue shape (kept
  there so dimension + JSON live together).
- `clarify-format.md`: ClarifyTicket request body, candidate shape,
  status transitions. Explicit: identity questions go here; field
  drift goes into DriftIssue.

Add `validators.md` documenting the four server-side validators the
LLM should self-check against before POSTing:

1. OntologyTypeValidator — type ids in allow-list
2. StructuralValidator — endpoint types + cardinality
3. EvidenceValidator — sourceReferences / intentMissing /
   implementationMissing requirement + DriftIssue surfacing
4. OrphanEdgeValidator — edge endpoints resolve

Each section names the validator's emitted ValidationCode strings so
the LLM can parse a 400 response and react.

braid-extract / braid-model / braid-clarify SKILL.md files already
forward-reference these docs (from C19); this commit makes the
references resolvable. One legacy comment in `packages/core/src/domain/ids.ts`
updated to point at the new doc names.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Earlier in this PR the structural validator only checked H2 heading
casing. H3 / H4 headings (Procedure step titles, sub-section
breakouts inside Step 2 of braid-extract, the four parts of the
braid-model procedure) still used sentence case:

    ### Step 1: fetch the scoped subgraph
    ### Step 3: submit the Proposal
    #### Policy emission

Sweep all five SKILL.md files plus the three shared docs
(proposal-format.md, clarify-format.md, drift-detection.md,
validators.md), the STYLE-GUIDE, and the server README so every
heading at every level uses Title Case (content words capitalised,
short articles / prepositions / conjunctions lowercase except as
first / last word). Code identifiers keep their canonical casing
(e.g. `Group Nodes by renderHint`).

The style guide §3 now spells the rule out explicitly so future
SKILL.md authors don't have to infer it from examples. The validator
still only enforces H2; H3 / H4 stays a review-time check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Earlier in this PR the built-in braid-core MCP gateway used the
streamable-http transport: user manually runs `uvx
openapi-mcp-gateway` in another terminal, exports
BRAID_MCP_GATEWAY_URL, server injects the URL into every spawned
skill's mcp-config. Stable but operationally awkward.

stdio is a strictly better default: gateway is claude's per-session
child process, lifecycle tracks the skill run, no long-running HTTP
server to babysit, no manual launch step.

Schema:

- Extend McpTransport to enum(['streamable-http', 'stdio']).
- Add McpStdioServerConfig { command, args?, env? } and a discriminated
  union McpServerConfig. Both transports stay first-class so workspaces
  can keep declaring third-party HTTP MCP servers (Redmine / Notion /
  Linear / …) alongside the stdio braid-core.

Server:

- mcpConfig.ts: toEntry() now emits either { type: 'http', url, headers }
  or { type: 'stdio', command, args, env } depending on transport.
- SubprocessSkillRunner: replace `coreGatewayUrl: string` with
  `coreGateway: { specUrl, uvxBin? }`. When set, the runner injects
  a stdio entry that runs `uvx openapi-mcp-gateway --transport stdio
  --spec <specUrl>`.
- composeFs.ts: drop BRAID_MCP_GATEWAY_URL. Preflight-check `uv` at
  boot (running `uvx --version`); when found, wire braid-core
  automatically with spec URL = `${apiUrl}/openapi.json`. When
  missing, log a one-line warning and skip the entry — skills
  requiring `braid-core` surface as not-ready in Studio rather than
  silently breaking. Honours BRAID_UVX_BIN for binary pinning.
- README.md: rewrite gateway section to reflect the new flow (no
  separate gateway terminal, just install uv).

Tests:

- schema/test/mcp.test.ts: cover both transport variants on the
  discriminated union (parse + reject paths).
- server/test/.../mcpConfig.test.ts: assert stdio entries shape
  correctly with command / args / env passthrough.

Forward-compat: when Braid grows a hosted product, both stdio
(claude-local) and streamable-http (claude-remote pointing at a
server-side gateway) paths work without further schema changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… URL

The first end-to-end run of /braid-extract failed with the gateway
throwing:

  ValueError: Server "braid-core": no base_url provided and none
  found in OpenAPI spec. Set base_url in config or add a servers
  entry to your spec.

The OpenAPI 3 spec @hono/zod-openapi emits via `app.doc()` was
missing the top-level `servers[]` block — without it,
openapi-mcp-gateway can fetch the spec but doesn't know where to
dispatch REST calls.

Thread the running server's apiUrl through createApp's options and
populate `servers: [{ url: apiUrl }]`. composeFsApp already knows
the apiUrl (it's the same one passed to SubprocessSkillRunner and to
the gateway's --spec arg), so wiring is a one-liner in server.ts.

Verified: `curl /openapi.json | jq '.servers'` now returns
`[{ "url": "http://localhost:4321" }]`, and re-running the gateway
no longer throws at startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Plugin.referenceDirs lets plugins ship shared docs alongside skills;
  DDD ships concept.md, core ships content-conventions.md.
- node.name max 200 + bilingual convention; node.description max 4000 +
  markdown (first block must be prose for Graph card preview); .describe()
  propagates to MCP inputSchema.
- Studio renders description via <Markdown>; GraphCanvas adds dimUnchanged
  + emphasizeAdded for proposal review.
- braid-clarify moves core -> ontology-ddd; all SKILL.md + shared/* trimmed
  under 150 lines, schema/inputSchema duplication removed.
- Remove tracked STYLE-GUIDE.md (kept locally as gitignored docs/).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Studio: <Markdown> renders mermaid blocks via lazy-loaded mermaid.js.
  Theme reads Studio CSS vars and routes through a 1x1 canvas to
  convert oklch -> rgb since khroma (mermaid's color parser) doesn't
  accept oklch.
- proposal-format.md: add "Picking sourceReferences" - generic rule
  to lead with the most representative entry per node type
  (definition / action / role) rather than a fixed intent-then-code
  order. braid-extract Step 3 cross-references.
- Skill prompts sweep: remove all arrows and em-dashes from SKILL.md
  and shared/*.md (30+ occurrences); replace with prose, colons,
  or bullets. Mermaid syntax inside fenced code blocks is preserved.
- content-conventions: "Prefer structure over run-on prose" with
  bullets / tables / mermaid as the suggested forms for enumerations,
  state machines, flows, comparisons.
- concept.md: "Translate, don't transliterate" rule - descriptions
  must use domain language, not DDD jargon (no "aggregate root",
  "consistency boundary", "ubiquitous language" in user-facing
  strings).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- EdgeDetailPanel mirrors NodeDetailPanel: type pill + From/To
  endpoint rows (click to jump to the node) + sources + center-in-
  graph footer.
- GraphSurface props gain selectedEdgeId / onSelectEdge;
  useGraphSurfaceState owns the mutual-exclusion invariant so
  callers can pass the setter pair through unchanged.
- GraphCanvas: edge selection becomes controllable; right-side
  aside switches between Node and Edge panels; adds centerOnEdge
  (midpoint of two endpoints).
- GraphTable: clickable edge rows; same aside switching logic;
  accepts emphasizeAdded and applies an inset emerald shadow to
  added rows so a small diff against a large graph still draws the
  eye in the table view.
- Graph + Proposals pass the new edge state through; Proposals
  keeps the mutual-exclusion invariant in its local setter pair.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Skill inputs:
- frontmatter `braid.inputs` declares one or more typed inputs
  (text / pick / multi-pick); SkillStructureValidator rejects
  duplicate input names.
- providers: static (inline options), graph-node, source-intent,
  clarify. Each pick declares a `fallback: text | disabled` for
  the zero-options case.
- GET /workspaces/:ws/skill-input-options dispatches per provider.
  source-intent walks workspace.rootPath + filters to markdown-like
  extensions so PNGs / lockfiles don't leak in.

Studio:
- ActionInputForm renders a typed form when frontmatter declares
  `inputs`; otherwise keeps the legacy textarea.
- MultiSelectDropdown (Radix Popover) replaces inline checkbox list:
  trigger button shows summary; popover has filter input + scrollable
  list + selected chips. Multi-pick submit fans out to N parallel
  runs sharing the same per-skill conversation key.

Storage durability:
- KuzuModelRepository.applyOperations forces a CHECKPOINT after
  writeDiff so the WAL is merged into model.kuzu before the call
  returns. ModelRepository gains an optional `close?` hook; the
  server wires SIGINT/SIGTERM to call it for graceful flush.

Apply race fix:
- New PerWorkspaceLock (Promise-chain mutex keyed by workspaceId).
- HITLService.applyProposal runs the load / validate / write / save
  chain inside the lock. Concurrent calls now serialise: the second
  sees status=applied and gets ConflictError, not a misleading
  "node already exists" from preview.

GraphCanvas:
- centerOnEdge useCallback moved before early returns so hook order
  is stable across renders (Rules of Hooks).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- New useMutualExclusionPair<A, B> hook collapses the node-vs-edge
  invariant from GraphSurface and ProposalPreview.
- New MultiSelectDropdown component lifts the dropdown UI out of
  ActionInputForm; props-only contract decoupled from skill schema.
  ActionInputForm shrinks 504 -> 349 lines.
- Local MultiPickField keeps the form-specific batch-run hint next
  to the dropdown so the generic component stays neutral.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mroops0111 mroops0111 changed the title Skills architecture: typed Actions form, dynamic providers, batch dispatch feat: rebuild skills architecture May 28, 2026
Mirrors openapi-mcp-gateway: pr.yml parses the PR title prefix
(feat, enhance, fix, docs, with `!` for breaking) and applies the
matching label, creating it if missing. release.yml groups release
notes by those labels.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mroops0111 mroops0111 changed the title feat: rebuild skills architecture enhance: rebuild skills architecture May 28, 2026
@mroops0111 mroops0111 merged commit 9586435 into master May 28, 2026
5 checks passed
@mroops0111 mroops0111 deleted the enhance/skills-architecture branch May 28, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant