enhance: rebuild skills architecture#8
Merged
Conversation
`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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
braid-extract/braid-clarify/braid-model) and a shared concept doc; core stays ontology-agnostic.建立訂單 (CreateOrder)) is supported. Studio renders both live.braid-generate-docrenders documentation for any container-shaped node type the active ontology declares (driven byrenderHint), not just DDD bounded contexts.text/pick/multi-pick) backed by static lists or live workspace data (graph nodes, intent documents, clarify tickets). Studio renders the typed form;multi-pickruns the skill in parallel across selected items.braid-coregateway, powered by OpenAPI MCP Gateway, replacing raw HTTP calls.