diff --git a/apps/arc-web/app/data/dot-grammar.ts b/apps/arc-web/app/data/dot-grammar.ts new file mode 100644 index 000000000..b3be1d95c --- /dev/null +++ b/apps/arc-web/app/data/dot-grammar.ts @@ -0,0 +1,106 @@ +import type { LanguageRegistration } from "shiki"; + +export const dotLanguage: LanguageRegistration = { + name: "dot", + scopeName: "source.dot", + fileTypes: ["dot", "DOT", "gv"], + firstLineMatch: "digraph.*", + patterns: [ + { + match: " ?(digraph)[ \\t]+([A-Za-z0-9]+) ?(\\{)", + captures: { + "1": { name: "storage.type.dot" }, + "2": { name: "variable.other.dot" }, + "3": { name: "punctuation.section.dot" }, + }, + }, + { + match: "(<|-)(>|-)", + name: "keyword.operator.dot", + }, + { + match: "\\b(node|edge|graph|digraph|subgraph|strict)\\b", + name: "storage.type.dot", + }, + { + match: + "\\b(bottomlabel|color|comment|distortion|fillcolor|fixedsize|fontcolor|fontname|fontsize|group|height|label|layer|orientation|peripheries|regular|shape|shapefile|sides|skew|style|toplabel|URL|width|z)\\b", + name: "support.constant.attribute.node.dot", + }, + { + match: + "\\b(arrowhead|arrowsize|arrowtail|color|comment|constraint|decorate|dir|fontcolor|fontname|fontsize|headlabel|headport|headURL|label|labelangle|labeldistance|labelfloat|labelcolor|labelfontname|labelfontsize|layer|lhead|ltail|minlen|samehead|sametail|splines|style|taillabel|tailport|tailURL|weight)\\b", + name: "support.constant.attribute.edge.dot", + }, + { + match: + "\\b(bgcolor|center|clusterrank|color|comment|compound|concentrate|fillcolor|fontname|fontpath|fontsize|label|labeljust|labelloc|layers|margin|mclimit|nodesep|nslimit|nslimit1|ordering|orientation|page|pagedir|quantum|rank|rankdir|ranksep|ratio|remincross|rotate|samplepoints|searchsize|size|style|URL)\\b", + name: "support.constant.attribute.graph.dot", + }, + { + match: + "\\b(box|polygon|ellipse|circle|point|egg|triangle|plaintext|diamond|trapezium|parallelogram|house|pentagon|hexagon|septagon|octagon|doublecircle|doubleoctagon|tripleoctagon|invtriangle|invtrapezium|invhouse|Mdiamond|Msquare|Mcircle|rect|rectangle|none|note|tab|folder|box3d|component|max|min|same)\\b", + name: "variable.other.dot", + }, + { + begin: '"', + beginCaptures: { + "0": { name: "punctuation.definition.string.begin.dot" }, + }, + end: '"', + endCaptures: { + "0": { name: "punctuation.definition.string.end.dot" }, + }, + name: "string.quoted.double.dot", + patterns: [ + { + match: "\\\\.", + name: "constant.character.escape.dot", + }, + ], + }, + { + begin: "(^[ \\t]+)?(?=//)", + beginCaptures: { + "1": { name: "punctuation.whitespace.comment.leading.dot" }, + }, + end: "(?!\\G)", + patterns: [ + { + begin: "//", + beginCaptures: { + "0": { name: "punctuation.definition.comment.dot" }, + }, + end: "\\n", + name: "comment.line.double-slash.dot", + }, + ], + }, + { + begin: "(^[ \\t]+)?(?=#)", + beginCaptures: { + "1": { name: "punctuation.whitespace.comment.leading.dot" }, + }, + end: "(?!\\G)", + patterns: [ + { + begin: "#", + beginCaptures: { + "0": { name: "punctuation.definition.comment.dot" }, + }, + end: "\\n", + name: "comment.line.number-sign.dot", + }, + ], + }, + { + begin: "/\\*", + captures: { + "0": { name: "punctuation.definition.comment.dot" }, + }, + end: "\\*/", + name: "comment.block.dot", + }, + ], + repository: {}, +}; diff --git a/apps/arc-web/app/routes/workflow-definition.tsx b/apps/arc-web/app/routes/workflow-definition.tsx index 68c7ed6fe..097451f55 100644 --- a/apps/arc-web/app/routes/workflow-definition.tsx +++ b/apps/arc-web/app/routes/workflow-definition.tsx @@ -1,3 +1,82 @@ -export default function WorkflowDefinition() { - return

Workflow definition will appear here.

; +import { useEffect, useRef, useState } from "react"; +import { useParams } from "react-router"; +import { workflowData } from "./workflow-detail"; +import { dotLanguage } from "../data/dot-grammar"; + +function CodeBlock({ + code, + lang, + filename, +}: { + code: string; + lang: string; + filename: string; +}) { + const containerRef = useRef(null); + const [ready, setReady] = useState(false); + + useEffect(() => { + let cancelled = false; + + async function highlight() { + const { createHighlighter } = await import("shiki"); + if (cancelled) return; + + const highlighter = await createHighlighter({ + themes: ["nord"], + langs: lang === "dot" ? [dotLanguage] : [lang], + }); + + if (cancelled) return; + + const html = highlighter.codeToHtml(code, { + lang, + theme: "nord", + }); + + if (cancelled || containerRef.current == null) return; + containerRef.current.innerHTML = html; + setReady(true); + } + + highlight(); + return () => { + cancelled = true; + }; + }, [code, lang]); + + return ( +
+
+ {filename} +
+ +
+ + {!ready && ( +
+          {code}
+        
+ )} +
+ ); +} + +export default function WorkflowDefinition() { + const { name } = useParams(); + const workflow = workflowData[name ?? ""]; + + if (workflow == null) { + return

No configuration found.

; + } + + return ( +
+ + +
+ ); } diff --git a/apps/arc-web/app/routes/workflow-detail.tsx b/apps/arc-web/app/routes/workflow-detail.tsx index e5fefb240..d8aa7624a 100644 --- a/apps/arc-web/app/routes/workflow-detail.tsx +++ b/apps/arc-web/app/routes/workflow-detail.tsx @@ -1,26 +1,253 @@ import { Link, Outlet, useLocation, useParams } from "react-router"; import type { Route } from "./+types/workflow-detail"; -export const workflowData: Record = { +interface WorkflowEntry { + title: string; + description: string; + filename: string; + config: string; + graph: string; +} + +export const workflowData: Record = { fix_build: { title: "Fix Build", filename: "fix_build.dot", description: "Automatically diagnoses and fixes CI build failures by analyzing error logs, identifying root causes, and applying targeted code changes.", + config: `version = 1 +task = "Diagnose and fix CI build failures" +graph = "fix_build.dot" + +[llm] +model = "claude-sonnet" + +[vars] +repo_url = "https://github.com/org/service" +branch = "main" + +[execution] +environment = "daytona" + +[execution.daytona.sandbox] +auto_stop_interval = 60 + +[execution.daytona.sandbox.labels] +project = "fix-build" + +[execution.daytona.snapshot] +name = "fix-build-dev" +cpu = 4 +memory = 8 +disk = 10 +`, + graph: `digraph fix_build { + graph [ + goal="Diagnose and fix CI build failures", + label="Fix Build" + ] + rankdir=LR + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + diagnose [label="Diagnose Failure", prompt="@prompts/fix_build/diagnose.md", reasoning_effort="high"] + fix [label="Apply Fix", prompt="@prompts/fix_build/fix.md"] + validate [label="Run Build", prompt="@prompts/fix_build/validate.md", goal_gate=true] + gate [shape=diamond, label="Build passing?"] + + start -> diagnose -> fix -> validate -> gate + gate -> exit [label="Yes", condition="outcome=success"] + gate -> diagnose [label="No", condition="outcome!=success", max_visits=3] +} +`, }, implement: { title: "Implement Feature", filename: "implement.dot", description: "Generates production-ready code from a technical blueprint, including tests, documentation, and a pull request ready for review.", + config: `version = 1 +task = "Implement feature from technical blueprint" +graph = "implement.dot" + +[llm] +model = "claude-sonnet" + +[vars] +spec_path = "specs/feature.md" +test_framework = "vitest" + +[setup] +commands = ["bun install", "bun run typecheck"] +timeout_ms = 120000 + +[execution] +environment = "daytona" + +[execution.daytona.sandbox] +auto_stop_interval = 120 + +[execution.daytona.sandbox.labels] +project = "implement" +team = "engineering" + +[execution.daytona.snapshot] +name = "implement-dev" +cpu = 4 +memory = 8 +disk = 20 +`, + graph: `digraph implement { + graph [ + goal="", + label="Implement" + ] + rankdir=LR + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + strategy [shape=hexagon, label="Choose decomposition strategy:"] + + subgraph cluster_impl { + label="Implementation Loop" + node [fidelity="full", thread_id="impl"] + + plan [label="Plan Implementation", prompt="@prompts/implement/plan.md", reasoning_effort="high"] + implement [label="Implement", prompt="@prompts/implement/implement.md"] + review [label="Review", prompt="@prompts/implement/review.md"] + validate [label="Validate", prompt="@prompts/implement/validate.md", goal_gate=true] + fix [label="Fix Failures", prompt="@prompts/implement/fix.md", max_visits=3] + } + + start -> strategy + strategy -> plan [label="[L] Layer-by-layer"] + strategy -> plan [label="[F] Feature slice"] + strategy -> plan [label="[P] Embarrassingly parallel"] + strategy -> plan [label="[S] Sequential / linear"] + plan -> implement -> review -> validate + validate -> exit [condition="outcome=success"] + validate -> fix [condition="outcome!=success", label="Fix"] + fix -> validate +} +`, }, sync_drift: { title: "Sync Drift", filename: "sync_drift.dot", description: "Detects configuration and code drift between environments, then generates reconciliation patches to bring everything back in sync.", + config: `version = 1 +task = "Detect and reconcile configuration drift across environments" +graph = "sync_drift.dot" + +[llm] +model = "claude-sonnet" + +[vars] +source_env = "production" +target_env = "staging" +drift_threshold = "warn" + +[execution] +environment = "daytona" + +[execution.daytona.sandbox] +auto_stop_interval = 120 + +[execution.daytona.sandbox.labels] +project = "sync-drift" +team = "platform" + +[execution.daytona.snapshot] +name = "sync-drift-dev" +cpu = 2 +memory = 4 +disk = 10 +dockerfile = """ +FROM ubuntu:24.04 +RUN apt-get update && apt-get install -y --no-install-recommends \\ + git curl ca-certificates jq diffutils \\ + && rm -rf /var/lib/apt/lists/* +RUN useradd -m -s /bin/bash daytona +USER daytona +WORKDIR /home/daytona +""" +`, + graph: `digraph sync { + graph [ + goal="Detect and resolve drift between product docs, architecture docs, and code", + label="Sync" + ] + rankdir=LR + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + detect [label="Detect Drift", prompt="@prompts/sync/detect.md", reasoning_effort="high"] + propose [label="Propose Changes", prompt="@prompts/sync/propose.md"] + review [shape=hexagon, label="Review Changes"] + apply [label="Apply Changes", prompt="@prompts/sync/apply.md"] + + start -> detect + detect -> exit [condition="context.drift_found=false", label="No drift"] + detect -> propose [condition="context.drift_found=true", label="Drift found"] + propose -> review + review -> apply [label="[A] Accept"] + review -> propose [label="[R] Revise"] + apply -> exit +} +`, }, expand: { title: "Expand Product", filename: "expand.dot", description: "Evolves the product by analyzing usage patterns and specifications to propose and implement incremental improvements.", + config: `version = 1 +task = "Propose and implement incremental product improvements" +graph = "expand.dot" + +[llm] +model = "claude-sonnet" + +[vars] +analytics_window = "30d" +min_confidence = "0.8" + +[execution] +environment = "daytona" + +[execution.daytona.sandbox] +auto_stop_interval = 180 + +[execution.daytona.sandbox.labels] +project = "expand" +team = "product" + +[execution.daytona.snapshot] +name = "expand-dev" +cpu = 2 +memory = 4 +disk = 10 +`, + graph: `digraph expand { + graph [ + goal="", + label="Expand" + ] + rankdir=LR + + start [shape=Mdiamond, label="Start"] + exit [shape=Msquare, label="Exit"] + + propose [label="Propose Changes", prompt="@prompts/expand/propose.md", reasoning_effort="high"] + approve [shape=hexagon, label="Approve Changes"] + execute [label="Execute Changes", prompt="@prompts/expand/execute.md"] + + start -> propose -> approve + approve -> execute [label="[A] Accept"] + approve -> propose [label="[R] Revise"] + execute -> exit +} +`, }, };