dippin pack --no-inline [--include <file-or-dir>...] (#73, #176). By default dippin pack inlines every command_file: / prompt_file: / system_prompt_file: body into the packed .dip, so a script that sources a sibling by relative path (e.g. . "${graph.workflow_dir}/scripts/lib/bootstrap.sh") has no file on disk in a packed run. --no-inline instead ships those directive targets as separate bundle entries under workflows/ and keeps the *_file: directives, so parser.ResolveFileDirectives resolves against the extracted tree exactly as in a source-tree run. --include <path> (repeatable, requires --no-inline) declares sibling assets the parser can’t discover statically — files referenced only from inside shell bodies — as a single file or a whole directory (recursive, via a symlink-refusing WalkDir); an --include that resolves to a .dip is an error. Assets ship under workflows/ as siblings of their referencing .dip (so relative resolution is identical source-vs-packed) and the bundle is stamped format_version 2. The library surface is dipx.Pack(ctx, entry, w, dipx.PackOptions{NoInline, Include}); the zero value reproduces the default inline behavior byte-for-byte (still format_version 1), and an older reader rejects a v2 bundle loudly with ErrUnsupportedFormatVersion. Path-safety is preserved throughout: every collected/included candidate is re-validated (containment + symlink refusal), directive targets are validated against their declaring .dip’s own directory (rejecting absolute paths and parent-escapes, matching source/inline behavior), assets ship 0644 (no mode carried), reserved manifest.json/manifest.sig names can’t be assets, and pack-time file-count + total-size caps mirror the open-time caps. Docs: docs/integration.md, the site cli page. Motivating use-case: collapsing many byte-identical inlined bootstrap preambles into one sourced helper (pipelines#49).cli page’s pack reference for --no-inline / --include and format_version 2; corrected the LSP hover/autocomplete capability descriptions (hover shows model/provider/command by node kind, not a prompt preview; completion offers node IDs + field names, not keywords) and the glossary’s human mode set (choice, freeform, interview, yes_no — confirm was never a mode); refreshed the editor-setup blog’s diagnostic range to DIP001–DIP010 + DIP101–DIP151; and added the missing agent/tool field keywords (writable_paths, last_response_truncate, response_format, response_schema, compaction_threshold, backend, working_dir, cmd_timeout) to the VS Code TextMate grammar.--no-inline produces a bundle whose extracted tree mirrors the source tree under workflows/. For a packed run to resolve ${graph.workflow_dir}/<relpath> identically to a source-tree run, the runtime must seed graph.workflow_dir to the unpacked-bundle root (tracker#430). dippin ships this independently and is not gated on the runtime (per never-gate-dippin-on-tracker); until the paired runtime lands, the dippin-side deliverable is purely that the extracted bundle byte-mirrors the source tree.else -> <node> edge default — the error-funnel collapse (#154, implements the decision in #135 / epic #127). One line at the bottom of the edges block, else -> Cleanup, is the graph’s success-side default destination: any node whose guard edges all fail to match, and which has no explicit unconditional edge of its own, routes there. It collapses the dominant line-count cost in real workflows — the per-marker failure funnel — from ~20 hand-routed edges to one. Stored as a graph-level ir.Workflow.ElseTarget (not a synthetic ir.Edge, so the edge list and the tools that read it — EdgesFrom, DOT export, edge-keyed analyses — are untouched); the validator seeds the else target into its reachability analysis explicitly (so DIP004 treats it as reachable and DIP105 sees it as a success route). At most one else per block (a second is a parse error). Lint: DIP102 (no default edge) and DIP101 (reachable only via conditional edges) treat a node covered by else as having its unconditional fallback, so they stand down. Structural validation: the else target must exist (DIP003) and is treated as reachable (DIP004), mirroring defaults.on_failure. Success-side only — else never intercepts a genuine node failure (tool exit ≠ 0, agent error), which routes via the failure cascade (on fail → defaults.on_failure); the two are distinct, non-competing channels. Formatter emits else last in the block; tree-sitter grammar + corpus updated (regen via npx tree-sitter generate). Docs: docs/edges.md, docs/GRAMMAR.ebnf, docs/llm-reference.md.dippin explain / diagnostic-text accuracy (#168, #172) — corrected diagnostic output that misdescribed behavior or suggested invalid values: DIP106 reworded “undefined variable” → “unrecognized variable reference” (it’s a namespace/shape check, not an upstream-write check); DIP109 retitled “duplicate subgraph reference” with the real trigger (two subgraph nodes sharing a ref:) and a remediation that actually silences it (different ref: / consolidate, not “distinct params”); DIP113/DIP114/DIP116 fix-hints corrected to the validator’s actually-valid value sets (retry-policy, fidelity, on_resume); explain examples now use valid .dip syntax — keyword edge form (when ctx.outcome = success) instead of the rejected bracket form ([success]), and # comments instead of //. Also fixed a stale simulate doc comment (“fail” is never emitted). Detection/text only — no rule-firing behavior changed.language/validation/analysis/architecture/cli/testing pages, glossary, skill.md + llm-reference (+ regenerated spec), and the tree-sitter/Zed/VS Code grammars. Added else syntax highlighting (and refreshed the stale Zed grammar pin), corrected DIP ranges/counts to DIP101–DIP151, documented previously-missing attributes (*_file: directives, tool_access, writable_paths, parallel block form, the four human modes), and fixed numerous stale command/flag/diagnostic descriptions.Condition.Parsed test fixtures (a DIP101 anti-pattern), unreachable defensive branches, de-facto-constant config knobs / single-use params / hand-rolled stdlib reimplementations, and duplicated simulate event-construction. No behavior change; net ~1000 lines removed.else adds one terminal step to the engine’s routing resolution: when a node’s guards do not match and it has no unconditional edge of its own, route to the section else target (success side). The failure cascade is unchanged and runs in its own channel. Inert on the dippin side until the paired runtime reads ir.Workflow.ElseTarget (per never-gate-dippin-on-tracker).Routing-syntax Phase 0 complete — the human-gate choice: routing key and the weight: soft-deprecation lint. All changes are dippin-side only; no behavior is gated on a paired runtime (per never-gate-dippin-on-tracker). Non-breaking: every valid v1 .dip file parses, validates, and formats unchanged — weight: still parses (carry-only) and merely warns now.
choice: edge attribute — a dedicated human-gate routing key that disambiguates the routing intent from a display-only label: (#130, #151). Carry-only: dippin parses, validates, formats, exports (a distinct DOT attribute), and round-trips choice: (ir.Edge.Choice) but assigns it no execution semantics — a paired runtime interprets it. Mirrors the carry-only override: shape (#125). DIP150 (Hint) flags a label-routing human gate (mode choice, yes_no, or the unset default) whose outgoing edge sets a non-empty label: but no choice: — in Phase 0 that label doubles as the load-bearing routing key, so the hint suggests marking it explicitly; freeform/interview gates (which route by text/answers, not labels) are exempt. Phase-0 source-compatibility (no dip 2 bump): label: is NOT made display-only — choice: wins as the routing key when present, and when absent label: still serves as the routing key, so existing human-choice workflows route unchanged. Editor surfaces (tree-sitter grammar + highlights, zed, vscode tmLanguage) updated.DIP151 (Warning) — soft-deprecation of the weight: edge attribute (#131, #152). weight: was tier 4 of the 5-level routing cascade — a speculative priority hint — but the cascade never consults it and no real .dip workflow uses it. The lint fires one diagnostic per edge with a non-zero weight:, steering authors toward conditions / on / a single unconditional fallback. Carry-only / no breakage: weight: still parses exactly as before — dippin only now warns. Removing the keyword and the cascade tier is deferred to dip 2 (#134). Surfaces in lint/check/watch/doctor.choice: unblocks the tracker’s convertEdge follow-up: once this is tagged, the engine prefers ir.Edge.Choice over Label as the human-gate matching key (falling back to Label when choice: is absent). Inert on the dippin side until the paired runtime reads it.site/content/{validation,language,cli}.md counts/ranges and the DIP148/DIP149 entries, on/loop/dip N/single-quote notes, tree-sitter/zed/vscode keyword highlighting, and the single-quoted STRING alternative in GRAMMAR.ebnf.Routing-syntax Phase 0 plus the dip 2 version foundation. All changes are dippin-side only — no behavior is gated on a paired runtime (per never-gate-dippin-on-tracker). Non-breaking: every valid v1 .dip file parses, validates, and formats unchanged (the one intentional rejection is a previously-silently-dropped unknown edge attribute, now diagnosed — see below).
on <token> edge routing shorthand — sugar for an equality test against a node’s natural outcome channel (#128, #140). A -> B on success desugars to when ctx.outcome = success for an agent source, and to when ctx.tool_marker = <token> for a tool source that declares marker_grep; nodes without a defined outcome channel (human gates, conditional, tool without marker_grep) get a located diagnostic suggesting when. IR-preserving — on X produces the identical ir.Condition as the equivalent when, so DIP102/DIP103, DOT export, and every other edge consumer reason over it unchanged. The channel is a single source of truth (ir.Node.OutcomeChannel()) shared by parser and formatter, so what the parser accepts is exactly what the formatter emits; dippin fmt rewrites an eligible when to on (shape-recognition, no new IR field) and is idempotent.loop keyword — a scannable, value-less synonym for restart: true on back-edges (#129, #143). A back-edge is the heaviest-semantics construct in the language (restart counter, max_restarts failure, downstream completion-state reset), so it earns a word: QualityGate -> Refactor on fail loop. Sets the same ir.Edge.Restart field — carried-only, no new IR, no semantics change; restart: true still parses and dippin fmt rewrites it to the canonical loop. DOT export (dashed) and DIP005 (cycle exemption) work for free via the shared field. Because loop is value-less it terminates a preceding condition unconditionally, so a bare loop is reserved on a condition RHS — write when ctx.x = "loop" (quoted) for the literal.DIP149 (Warning) — ambiguous routing: a node with ≥2 unconditional outgoing forward edges, whose winner is decided only by the cascade’s lexical tiebreak on target node IDs (so renaming a target can silently change which edge fires) (#132, #144). Conservative (favors false negatives): restart/loop back-edges are excluded; a guarded edge plus a single unconditional fallback is the intended pattern and is not flagged; duplicate same-var/same-value guards stay covered by DIP103. parallel fan-out (all edges fire) and human choice gates (the human picks) are exempt; fan_in is not exempt — its out-edges route by the ordinary cascade. Surfaces in lint/check/watch/doctor. Sets up the Phase 1 cascade-collapse where the lexical tier is removed.dip N format-version declaration + fmt --migrate scaffolding (#133, #148). A leading line-1 dip N sets the parsed format version (default 1), wiring the previously-dead ir.Workflow.Version; the formatter emits the line only for N>1, so existing v1 files stay declaration-free and round-trip byte-identical. fmt --migrate lands as a no-op v1→v1 identity pass reusing the existing parse/format machinery. Foundation only — p.version is now reachable from parse_edges.go; the v1→v2 transforms (edges own their destinations) ship with #134 and are explicitly NOT in this release.label: attributes and when conditions (#146, closes #120). Extends the v0.39.0 single-quote scalar support to the edge token-stream surface that #117 deliberately deferred. YAML-style (surrounding quotes stripped, '' → ', backslashes literal); the token-stream comment stripper now treats ' as a delimiter only at a token-start boundary, so a prose apostrophe (it's) still strips its trailing comment while a # inside a quoted token stays literal. Normalizes to double-quoted output on fmt.A -> B bogus: true previously parsed with bogus dropped on the floor (the same inert-attribute class that hid the pre-#124 override); it now emits exactly one located diagnostic. Colon-gating means label/weight/restart/override terminate a when condition only when followed by :, so a bare attribute word as a condition’s right-hand value (when ctx.reason = override) is no longer truncated. Source-compatible but not strictly non-breaking — a file with a typo’d or runtime-only edge attribute that parsed before is now rejected; this is the intended hardening that makes the Phase 0 keyword surface safe.--extra-models are now scoped out of the global model registry (#105, #145). validator.RegisterExtraModels mutated a package-level catalog, so models from one lint/doctor run leaked into every later validate/doctor/lint call in the same process (making TestCmdLint_ExtraModels_SuppressesDIP108 non-idempotent under -count>1). Replaced with a pure ParseExtraModels threaded through a scoped validator.Options/LintWithOptions (and doctor.DiagnoseWithOptions); Lint/Diagnose keep their signatures and delegate with empty options.startswith edges now count as covering printf-format markers in coverage analysis (#141, #147). dippin coverage (which drives dippin doctor’s “uncovered outputs” check) did exact-match of each tool-output literal against edge-condition values, discarding the operator — so a when ctx.tool_stdout startswith citations-fail edge never covered the printf-format output citations-fail-%s, capping doctor grades on otherwise-clean files. The operator is now carried through and an output is covered if some startswith term is a prefix of it (conservative — only startswith is extended). The public EdgeConditions []string report field is unchanged.cmd/wasm marshalled the raw validator.Diagnostic (PascalCase keys, numeric severity), so the playground JS read undefined for code/severity/message and crashed on the default workflow’s DIP144 — mislabeled as “Failed to load WASM”; diagnostics now map to the camelCase/string-severity DTO matching the CLI contract. Separately, the initial highlight is deferred to DOMContentLoaded so the default source is no longer black-on-black until the first keystroke.Carry + detection work pairing with downstream runtimes — no dippin behavior is gated on runtime readiness (per never-gate-dippin-on-tracker). Both attributes are carried through every .dip path; their semantics are owned by a paired runtime.
last_response_truncate (agent node + parallel branch override, integer) — a character-count cap on the auto-injected ${ctx.last_response} handoff, a carry-only chain-attack mitigation (#56, #123). Carried through parse → IR → formatter → DOT export → migrate; DIP148 (Warning) flags a negative value (0/unset = disabled). Carried, not interpreted — a paired runtime performs the truncation. Mitigates the ${ctx.last_response} auto-injection vector of #56 when an enforcing runtime honors this field; DIP147 (explicit-key handoff) is unaffected and there is no DIP147↔truncate interaction.override: true / override: false edge attribute → ir.Edge.Override (bool) — carried, not interpreted (the same policy as restart:) (#124). Parsed in any order alongside when / label / weight / restart (no longer silently swallowed), and round-tripped through the .dip formatter, DOT export, and migrate. No dippin-side semantics — a paired runtime maps it onward and owns the rule that override edges originate from human nodes. Same shape as the params: carry shipped in v0.39.0 (#113).override unblocks the workflow-author syntax for 2389-research/tracker#271, whose engine half (the validation_overridden terminal status, pipeline.Edge.Override, sticky checkpoint persistence, --fail-on-override, lint TRK102) shipped in tracker v0.35.0. Once this is tagged, tracker maps ir.Edge.Override → pipeline.Edge.Override in convertEdge, adds the human-origin validation (tracker spec D11), and migrates the catalogued override edges (tracker#271 finisher).last_response_truncate is carried + linted by dippin but the truncation is performed by the runtime — inert until a paired runtime reads it.Detection, carry, and correctness work — no paired runtime release required (per never-gate-dippin-on-tracker). DIP147 and the params: carry are dippin-side detection / round-trip only; the single-quote fix is a parser correctness fix; the catalog refresh is data.
DIP147 (Hint) — chain-attack detection: a restricted agent’s output is laundered through an explicitly-keyed context handoff into a downstream tool-bearing agent, re-granting capability the upstream node was denied (#56, #115). Covers the explicit-key handoff vector only — a partial #56. The other laundering vectors remain open follow-ups: ${ctx.last_response} auto-injection (and a last_response_truncate: mitigation), attribute, cross-file, and branch-override. Detection, not runtime enforcement.params: on parallel / fan_in nodes are now carried through parse → IR → formatter round-trip (#110, #113). Formatter round-trip only (not DOT export or migrate); a paired runtime owns any fan-in policy semantics.claude-opus-4-8, claude-fable-5, claude-mythos-5, and claude-mythos-preview are recognized models (clearing a spurious DIP108), with claude-opus-4-8 ($5/$25), claude-fable-5 ($10/$50), and claude-mythos-5 ($10/$50) priced so dippin cost can estimate them. Deprecation metadata refreshed (claude-opus-4-1 retires 2026-08-05; claude-opus-4-0 migration target → claude-opus-4-8). The richer cache/batch/fast-mode pricing schema is an explicit non-goal; tracker-side runtime support is a cross-repo follow-up.marker_grep: '^(a|b)$' was stored with the literal ' chars — and the formatter then re-wrapped it in double quotes — silently breaking the runtime regex. Single quotes are now YAML-style (surrounding quotes stripped, '' → ', backslashes literal), and a # inside '...' is no longer mistaken for a trailing comment. Fixed in the parser so every path (lint / pack / format / DOT / migrate / runtime) benefits; the tree-sitter grammar admits single-quoted strings too.Cross-file tool_access advisory completeness. Both changes are dippin-side detection only — they refine the DIP146/DIP143 cross-file analysis shipped in v0.37.0 and require no paired runtime release (per never-gate-dippin-on-tracker, no dippin behavior is gated on runtime readiness).
DIP143 (Hint) deep cross-file advisory — the native dippin lint cross-file pass now emits DIP143 for a partial-audit or unresolvable child found at depth ≥ 1 (behind an already-audited intermediate), gated by path tool_access intent (#102). Previously this fallback came only from the entry-file lint, so a partial/unresolvable gap more than one hop deep was silent — DIP146 already traversed transitively for zero-intent children, but the partial/unresolvable advisory did not. Reuses DIP143 (no new code); the message distinguishes partial-audit (resolved — a tool-bearing agent lacks its own tool_access) from unresolvable (missing / unparseable / refused). Entry boundaries (depth 0) keep their existing validator.Lint DIP143. Emitted from dippin lint/check/watch; no wasm/validator-detection change.tool_access intent and later via a restricting path was not re-walked under the restricting intent, so a zero-intent / partial-audit / unresolvable grandchild below the shared child was silently missed (#109). The recursion guard is now intent-aware (re-walk once on a no-intent → intent upgrade; bounded and cycle-safe, with the entry file never re-walked). Pre-existing in DIP146 since its introduction (#89) and inherited by the #102 deep advisory; both now catch the mixed-intent shared-child shape regardless of traversal order.DIP143 advisory and the intent-aware cross-file traversal in the validation reference (docs/validation.md + the website validation page).Mixed release. The cross-file tool_access detection + resolver hardening and the DIP010 edge-condition error are dippin-side detection only (no paired runtime release required). The graph-level on_failure route and the declarable budget guards are an authoring surface dippin carries + lints while a paired runtime enforces — inert until the runtime reads them (per never-gate-dippin-on-tracker, no dippin behavior is gated on runtime readiness).
DIP146 (Hint) — cross-file tool_access gap: a manager_loop (subgraph_ref) or subgraph (ref) boundary delegates to a child .dip whose agents re-grant tools a parent on the path restricted (#89). Completes the DIP143 (#59) arc by resolving and classifying the referenced child instead of only flagging the boundary: zero-intent child → DIP146 (DIP143 superseded); full-restrict or agent-less → silent (confirmed safe); partial-audit or unresolved → DIP143 retained (“unknown” never reads as “checked & safe”). Full transitive DFS with EvalSymlinks-keyed cycle termination and a depth cap. Emitted from the native CLI pass — dippin lint/check/watch apply it with DIP143 supersession; validate stays structural-only. Detection, not runtime enforcement.DIP010 (Error) — an edge when condition that cannot be parsed (e.g. an unknown operator, or a tool-node field like marker_grep used in operator position) (#98). Previously the parse error was discarded by Lint() and EnsureConditionsParsed stopped at the first bad edge, so validate/lint/check/doctor greenlit a workflow that hard-fails at dippin simulate — and every edge after the first bad one silently lost its AST-dependent lints (DIP103/120/121/122). DIP010 is emitted from validator.Validate(), so every command path catches it, and parsing now continues past failures (one diagnostic per bad edge; later edges keep getting linted). Edge conditions only; manager_loop node conditions are a separate follow-up.on_failure: <NodeID> — a graph-level (defaults:) catch-all failure route (#92). Structurally validated (DIP003 existence with a “did you mean?” suggestion; the target is seeded into DIP004 reachability so a recovery node reachable only via the catch-all is not falsely flagged unreachable) and round-tripped through the .dip formatter, DOT export, and migrate.DIP144 (Warning) — an agent node has no failure route (#93). Suppressed by an explicit ctx.outcome = fail|failure edge, fallback_target, bounded retry (retry_target + max_retries > 0), or a graph on_failure; an unconditional/success edge does not suppress it.stall_timeout (graph defaults:, duration) — abort/route when no forward progress is made for a wall-clock span (#94); 0/unset = disabled. Round-trips through parser → formatter → DOT export → migrate (this also closed a pre-existing DOT-export bug that silently dropped every budget ceiling). max_turns now has defined exhaustion semantics: outcome = fail, routed through the failure cascade (docs-only; no new action field).DIP145 (Warning) — a graph budget default is negative (#94); 0 = unset = no warning.examples/on_failure_route.dip and examples/budget_guards.dip demonstrators..dip refs, reaching parity with the pack walker (#100). Fail-soft: a refused child is treated as unresolvable (DIP143 retained), so linting never hard-errors or aborts. Reuses dipx’s ReadNoFollowSymlinks (leaf + ancestor-directory symlink refusal) and a fixed entry-file-directory containment root; absolute refs are re-rooted under that root rather than read out of tree. Lstat-based parity with the pack walker — no O_NOFOLLOW/build-tag complexity and no wasm impact. Detection parity, not runtime enforcement.dippin doctor now exits non-zero when the report contains errors (e.g. DIP010 or any structural DIP001–DIP010), rather than always exiting 0. The report still renders in full; only the exit code changes, so doctor no longer greenlights a workflow that cannot execute.on_failure, stall_timeout, and max_turns exhaustion are carried + linted by dippin but enforced by the runtime — inert until a paired runtime reads them. The runtime owns the failure-cascade ordering: matching fail edge → bounded node retry → node fallback_target → graph on_failure → halt.Dippin-side only — no paired runtime release required.
DIP143 (Hint) — a manager_loop (subgraph_ref) or subgraph (ref) node references a child .dip that does not inherit the parent workflow’s tool_access restrictions (#59). Fires only when the workflow declares tool_access intent (any non-empty value on an agent or parallel branch) and references an external subgraph, reminding the author to give the child’s agents their own tool_access. tool_access is per-node and does not cross a file boundary. A direct self-reference (a node whose ref resolves to its own source file) is not flagged. The lint never parses the child file (the validator may not import the parser); real cross-file effective-access enforcement, plus transitive cross-file cycles, are deferred to #89.@file directive resolver is hardened against a leaf TOCTOU race (#79). The path was validated and then read via a separate os.ReadFile, so a concurrent symlink/rename swap of the final component could redirect the read outside baseDir, bypassing the #67/#77 containment under a race. Now a single open-once path — os.OpenFile(O_RDONLY|O_NOFOLLOW) → f.Stat() (fstat the fd) → containment check → io.ReadAll — operates entirely on one fd, closing the check-to-read race. On Unix, O_NOFOLLOW makes leaf-symlink rejection atomic (ELOOP); the resolved path is never leaked in the error. The residual parent-directory swap race is documented as out of scope.tool_access is node-scoped — it constrains the executor of a single node and does not taint downstream nodes — resolving #57 via documentation rather than a graph-topology lint (#87).writable_paths: — a comma-separated glob list on agent nodes and parallel branches that bounds where an agent’s tools may write (e.g. writable_paths: workspace/**, .ai/sprints/**) (#75). Absent = unbounded; a per-branch empty value inherits the target agent’s (never resets to unbounded). dippin carries + lints; the runtime enforces an fs-level write jail on the native backend (covering Write/Edit/ApplyPatch, Bash and any process Bash spawns), resolved against an immutable session root. Distinct from the advisory writes: field (context keys). Carried through parse → IR → format → DOT export → migrate.DIP141 — writable_paths nullified by tool_access: none (dead config; nothing left to bound).DIP142 — unsafe writable_paths entry (absolute / ~ / Windows-drive / ..-escape / brace mis-split). Author-clarity lint; the runtime fs-jail is the real boundary.examples/agent_writable_paths.dip demonstrating the five motivating failure/recovery-recorder shapes.working_dir/Params cannot relocate it), Params/working_dir bypass defense, and a single-turn red-team suite.writable_paths: is rejected by dippin validate/pack (parse error). A malformed or runtime-unrecognized value → the runtime denies all writes or refuses to start, never unbounded. On claude-code/acp, the runtime refuses to start when writable_paths is set (native-only enforcement) — never a prompt-level pretend-jail.writable_paths must refuse to start rather than run unbounded. Pin an enforcing runtime, never @latest.curl/cargo fetch), reads/exfil-by-read, or content within an allowed path (a workspace/** grant can still poison workspace/Cargo.toml). Chain laundering is tracked in #56.docs/superpowers/specs/2026-05-29-issue-75-writable-paths-design.md.prompt_file: <path> and system_prompt_file: <path> directives on agent nodes (#65). Symmetric extension of v0.33.0’s command_file:; same path-relative-to-.dip rules and security cap. dippin pack inlines the content, so no runtime coordination is required.examples/external_prompts.dip demonstrating both directives.dippin fmt --write previously stripped inline system_prompt: from agent nodes on save (latent since the field was introduced). If you’ve been re-adding system_prompt: after running fmt, this fix is for you.*File set, content empty), which is the correct view for those contexts.defaults agent does not currently support prompt / system_prompt fields, so file-form-in-defaults requires a larger design — filed as #72..dipx redesign considered and explicitly deferred so v0.34 stays dippin-only; filed as #73.New command_file: directive on tool nodes replaces inline command: heredocs with external file references. Solves the heredoc-bloat pattern seen in long runtime workflows. Dippin-only release — no runtime coordination required (the runtime reads inlined Command from .dipx bundles unchanged).
command_file: <path> directive on tool nodes. Path is relative to the .dip source directory.parser.ResolveFileDirectives(w, baseDir) — separate-pass file loader called by CLI entry points. Parser itself stays pure.Lstat), 4 MiB size cap. Error messages reference user-written paths, not resolved absolute paths.command: and command_file: are set on the same tool node.examples/external_files.dip + examples/external_files/setup.sh demonstrate the directive.cmd/wasm (playground) skip the resolver. They see cfg.CommandFile != "" && cfg.Command == "", which is the correct unresolved-IR view.command_file: to inline command:. The runtime reads from .dipx bundles where content is already inlined; no current consumer needs the path preserved through DOT. Deferred to a follow-up issue (#69).New agent-node safety primitive: tool_access: none strips an LLM’s tool catalog. Coordinated runtime release — the dippin field is meaningless without runtime enforcement, so they ship together (see #41 for context, including the v0.28.2 runaway-agent incident this bounds).
tool_access: field on agent nodes. One explicit value: none (no LLM tools). Omitted = full catalog (current behavior).tool_access values.examples/agent_tool_access.dip demonstrates the field on a summarizer node.tool_access: field is a no-op (parking decision: lint-validated runtime-no-op safety fields ship as worse-than-nothing — the coordinated release prevents this).tool_access: none is set.tools array via tool_choice: none.Params keys (allowed_tools, disallowed_tools, tool_choice, permission_mode) are not honored when tool_access: none is set — Params bypass defense.tool_access: none or refuses session creation with a clear error.tool_access: none produces zero executions (the actual v0.28.2 shape).Two small dippin-internal fixes. Closes #45, #49.
mode: yes_no on human nodes no longer trips DIP127. The runtime supports yes_no as a documented mode; dippin’s validator now accepts it alongside choice, freeform, and interview. The four-mode list is cascaded through the validator help text, the DIP127 explanation, docs/validation.md, docs/nodes.md, docs/llm-reference.md, the hosted skill (site/static/skill.md), the LSP completion tooltip, the integration guide, and the IR field comment.start: node no longer collapse to AgentConfig after a .dip → DOT → .dip round-trip. migrate.resolveStartExitKind now recovers NodeTool from the start-marker Mdiamond shape by sniffing tool-specific DOT attributes (tool_command, marker_grep, outputs, route_required, output_limit).None. Both fixes are dippin-internal; the runtime is unaffected.
Coverage extractor now respects shell redirection. Closes #40.
dippin coverage no longer flags file-redirected echo/printf statements as uncovered tool outputs. The extractor switched from regex matching to an AST walker built on mvdan.cc/sh/v3/syntax.>, >>, &>, >&), feed into pipes, or are nested inside command substitution are now correctly skipped. Pipelines using the “log to file, printf marker on stdout” pattern flip from partial to covered.Three follow-ups to v0.28.0’s tool-routing surface. Closes #42, #43, #44.
DIP138 reserved for a future advisory: “tool node routes on stdout but declares no marker_grep / outputs”. Code + description + explanation entry only; no firing logic in this release.outputs: now survives a .dip → DOT → .dip round-trip. DOT export emits outputs="a,b,c" on tool nodes that declare outputs, and dippin migrate dot→.dip reads it back.DIP101 / DIP102 no longer fire on tool nodes that declare marker_grep:. Those nodes route via the typed ctx.tool_marker channel — outgoing conditional edges on them are intentional routing, not unsafe reachability. Removes the false-positive coverage hit that the runtime’s TRK101 option (d) guidance triggered.goal_gate, auto_status, cache_tools, route_required) now accept true/false, 1/0, yes/no, on/off case-insensitively via a new shared parseBoolAttr helper. Anything else now produces a parse diagnostic instead of silently coercing to false. The migrate (DOT-input) path keeps strict equality since DOT attrs are machine-emitted.None. All changes are dippin-internal; the runtime is unaffected.
docs/nodes.md “Markers and Verbose Output” notes the DIP101/DIP102 suppression and the accepted boolean forms.docs/llm-reference.md common-mistakes table cross-references the suppression behavior.site/static/skill.md) updated to match.Tool-node routing fields land in the parser and IR. Authors following the runtime’s TRK101 recommendation can now declare marker_grep, route_required, and output_limit directly in .dip source. Closes #39.
tool.marker_grep — regex matched line-by-line against captured stdout; populates ctx.tool_marker at runtime.tool.route_required — boolean; when true, the node fails with EventToolRouteMissing if the command emits no routing signal recognized by the runtime.tool.output_limit — non-negative integer (bytes); 0 uses the engine default stdout tail-window. dippin fmt omits the field when the value is zero.ctx.tool_marker, ctx.tool_route.migrate/parity.go compareToolConfigs now compares all ToolConfig fields. Pre-existing Timeout / Outputs parity gaps backfilled.These fields pass through DOT export to the runtime. Routing semantics require the runtime to ship the matching extractToolAttrs change; see issue #39 for details.
site/content/blog/whats-new-v028.md.docs/nodes.md gains a “Best” subsection in Markers and Verbose Output demonstrating marker_grep.site/static/skill.md) updated with new context variables and best-practice bullet.Model catalog and pricing verification pass against canonical provider docs. Last verified: 2026-05-18 in validator/lint_model.go and cost/pricing.go. No breaking changes to public APIs — but the cost table values move and the catalog accepts new IDs, so downstream tooling that snapshots dippin’s data should re-snapshot.
gpt-5.5, gpt-5.5-pro, gpt-5.4-pro, gpt-5.2-pro, gpt-5, gpt-5-pro, gpt-5-mini, gpt-5-nano.grok-4.3 ($1.25/$2.50, current flagship).deepseek-v4-flash ($0.14/$0.28), deepseek-v4-pro ($1.74/$3.48 list — 75% launch discount through 2026-05-31).gemini-3.1-flash-lite (GA promotion of the preview variant, same price).mistral-medium-3-5-2604 ($1.50/$7.50, new flagship-class), mistral-medium-3-1-2508 ($0.40/$2.00), Ministral 3 generation (ministral-3-3b-2512 $0.10/$0.10, ministral-3-8b-2512 $0.15/$0.15, ministral-3-14b-2512 $0.20/$0.20).command-r-08-2024, command-r-plus-08-2024, command-r7b-12-2024 (the canonical recommended form; bare aliases kept callable but flagged as resolving to versions deprecated 2025-09-15).gpt-5.2 $0.875/$7 → $1.75/$14, gpt-5.1 $0.625/$5 → $1.25/$10, gpt-4.1-mini $0.20/$0.80 → $0.40/$1.60. Newer mini/nano tiers and the o-series held steady.grok-4.20-0309-reasoning, grok-4.20-0309-non-reasoning, grok-4.20-multi-agent-0309 all $2/$6 → $1.25/$2.50, matching grok-4.3’s tier.deepseek-chat and deepseek-reasoner are compatibility aliases resolving to V4-Flash; priced at $0.14/$0.28 (down from $0.28/$0.42) to match the redirect target.claude-haiku-3-5 repriced to $0.80/$4.00 (Bedrock/Vertex passthrough rate; was $0.25/$1.25 in the catalog). Model was retired on the first-party API 2026-02-19; remains available via Bedrock and Vertex AI.Hard-retired models that the provider returns errors for — calling them is a real bug, DIP108 surfaces:
pixtral-large (deprecated 2026-02-27), mistral-small-3.2 (deprecated 2026-04-30, past sunset).gemini-3-pro-preview (shut down 2026-03-09).Soft-retired models that the provider silently redirects (kept in the catalog, priced at the redirect target so cost analysis stays accurate):
grok-4-1-fast-reasoning and grok-4-1-fast-non-reasoning (retired 2026-05-15; xAI redirects to grok-4.3 server-side and bills at grok-4.3 rates).Still in the catalog with deprecation comments:
gemini-2.0-flash.claude-sonnet-4-0, claude-opus-4-0.deepseek-chat, deepseek-reasoner aliases.gpt-4o, gpt-4.1-nano, o3-mini, o4-mini.Inline comments in cost/pricing.go flag values held pending re-verification:
nemo and mistral-small-2603: official pricing tab is JS-rendered; third-party sources conflict.command-a-03-2025 and command-r7b-12-2024: per-token pricing removed from the public page.site/content/blog/whats-new-v027.md covers the refresh.site/content/blog/whats-new-v026.md retrospective on the v0.26 requires: keyword.related: cross-references updated.requires: keyword. New optional workflow-header field for declaring workflow-level prerequisites (e.g., tools, MCP servers, env vars) as a comma-separated identifier list. Advisory in v1 — parsed, round-tripped by the formatter, and exposed as ir.Workflow.Requires []string, but not yet validated by lint. Mirrors the shape of node-level reads: / writes:. Filed to unblock the --git= preflight mechanism in the runtime. Canonical formatter order is goal → requires → start → exit. Editor support (tree-sitter, VS Code, Zed) and the hosted skill (site/static/skill.md) updated..dipx format v1.1. The spec at docs/superpowers/specs/2026-05-06-dipx-bundle-format-design.md is the canonical contract; this release closes ambiguities in it (Bundle 6), brings the implementation in line with the documented contract, and adds genuine cancellation through Pack/Open hot paths.
Breaking changes for downstream consumers:
Source.Workflow now takes context.Context as its first argument. Bump your dippin-lang import via go install [email protected] (or @latest) and update call sites.dippin inspect --format=json status field is now an object, not a bare "VALID" string. If you parse the JSON in scripts, decode status as an object with valid, verify_skipped, file_count, byte_total, format_version.dipx.Open previously DFS’d the ref graph rooted only at m.Entry, while parseAllWorkflows already parsed every manifest-listed workflow. A cycle in a manifest-listed-but-entry-unreachable workflow could slip through. walkRefs now iterates detectCycles over m.Files. (Bundle 6 / Phase 5 L2/L3.)dippin pack/unpack/inspect exit code 2 (integrity failure) now matches the spec contract. isIntegrityErr previously routed only 5 sentinels to exit 2; 7 others (ErrUnsupportedFormatVersion, ErrFileMissing, ErrFileUnexpected, ErrEntryNotInManifest, ErrRefEscape, ErrRefCycle, ErrCapExceeded, ErrPathUnsafe) defaulted to user-error 1. Refactored to a sentinel-slice + loop covering all 12 spec-enumerated sentinels. (Bundle 6 / Phase 8 M1.)Open enriches manifest-decode errors with the bundle path. BundleError.Path for ErrManifestInvalid and ErrUnsupportedFormatVersion was previously empty or a JSON field name (e.g., "format_version"); external callers now always observe the bundle file path. The original Path is preserved in Detail when non-empty. (Bundle 5 / Phase 3 manifest-decoder error-context.)Pack subgraph parse failures attribute to ErrSubgraphParse. Previously every parse failure surfaced as ErrEntryParse regardless of which workflow failed; subgraph failures now correctly classify as ErrSubgraphParse with the subgraph’s filesystem path. (Bundle 5 / P10.9.)dippin inspect emits a structured status object. JSON output’s status was previously a bare string "VALID"; it is now an object with valid, verify_skipped, file_count, byte_total, format_version per spec § “CLI / inspect command”. Text footer now includes the byte total. Breaking for JSON consumers parsing status as a string. (Bundle 2 / Phase 8 M4, L1, L2.)dippin inspect --no-verify actually skips hash verification. Previously a no-op (warning printed, full verification still ran). Now routes through a new dipx.OpenManifest API that performs only structural-admission steps; tampered bundles can be inspected without integrity errors firing. (Bundle 2 / Phase 10 P10.4.)Source.Workflow now takes context.Context as its first argument. dirSource.Workflow checks ctx before disk I/O; Bundle.Workflow checks ctx at entry for interface consistency. Breaking for external callers (the runtime) — bump your dippin-lang import to pick up the new signature. (Bundle 1 / Phase 6 L4.)Open and Pack are cancellable mid-loop. verifyAllHashes, walkSourceTree, and writeBundle now check ctx.Err() between each entry/iteration. A long Open against a many-entry bundle or a long Pack against a deep source tree can be canceled within one entry’s processing time instead of running to completion. (Bundle 1 / Phase 10 P10.2, P10.7, P10.10.)Seven .dipx bundle-format spec clarifications (no behavior change beyond the two Fixed items above). Each is described in detail in the per-commit messages on this branch.
\ MUST be rejected” (was “Backslash \ and any other separator…”). The implementation already rejects only backslash; the spec wording was over-broad.BundleError.Path semantics across three real cases: bundle-relative (read-side, post-Open), JSON field name (manifest decode pre-bundle-context), source filesystem path (Pack-side). Spec now requires Open to enrich (b) → (a) before returning.ErrFileUnexpected added to the error precedence list at category 4.parseAllWorkflows.inspect --format=json status object schema documented with a concrete JSON example (valid, verify_skipped, file_count, byte_total, format_version).Source.Workflow(ctx, sub.Ref, parentPath) (was missing ctx). Bundle 1 will land the matching Go signature change..dipx bundle format — deterministic, content-addressed ZIP that packages a .dip entry workflow plus every transitively-reachable subgraph into a single integrity-verified artifact. Bundles carry a SHA-256-per-file manifest and a workflow-tree identity hash; integrity is verified on every Open. New package dipx/ exposes Open, OpenLax, OpenReader, Pack, Extract, Validate, and Load, plus the Source interface (Entry, Workflow) for runtime consumers.dippin pack <entry.dip> (build a bundle, with -o, --dry-run); dippin unpack <bundle.dipx> (atomic extract via staging + rename, with -o, --force); dippin inspect <bundle.dipx> (print manifest, identity hash, file list; --format text|json)..dipx — validate, lint, doctor, parse, cost, coverage, simulate, optimize, unused, graph, diff, check, explain, export-dot now transparently load a .dipx via dipx.Load, hash-verify it, and analyze the entry workflow.0 (ok), 1 (user error), 2 (integrity error), 3 (I/O error), 4 (cancelled).dipx may import ir + parser + simulate but is forbidden from importing validator, cost, formatter, or any other analysis package. Pack-time structural validation runs at the CLI layer (cmd/dippin/cmd_pack.go).Extract --force no longer destroys the existing destination directory when the staging-into-place rename fails on a cross-device boundary (EXDEV). The new backup-aside / rename-into-place / remove-aside sequence preserves the original on failure.Pack rejects symlinked parent directories anywhere between the entry’s source root and a leaf .dip, closing a host-file exfiltration vector when packing untrusted source trees (CI-runner contributor builds, mono-repo subdirs).Pack’s ref-escape check no longer false-positives on legitimate filenames whose component name begins with .. (e.g., ..foo/bar.dip). The check now requires the literal .. component, not a .. substring.dippin pack -o foo.dipx no longer races two parallel invocations against the same temp filename — uses os.CreateTemp for a unique staging path.golang.org/x/text from v0.3.3 to v0.37.0 (defensive — only unicode/norm is consumed; the v0.3.3 CVEs were in x/text/language).WorkflowDefaults tool-safety fields: tool_commands_allow (glob allowlist for tool-node shell commands) and tool_denylist_add (globs appended to the runtime’s default denylist). Both round-trip through parser → formatter → DOT export → migrate. Values pass through verbatim — the runtime owns split and glob semantics. (#28)ExportDOT now emits graph-level attributes (rankdir, tool-safety defaults, workflow vars) as a single graph [key=val, ...]; block instead of separate bare statements (rankdir=TB;). This is the form the migrate DOT parser accepts, enabling true .dip → DOT → .dip round-trips. The output remains valid DOT; consumers that only render via Graphviz are unaffected.tool_commands_allow / tool_denylist_add in vars: no longer emitted — before this release, these keys weren’t reserved, so a workflow that smuggled them through vars: would have them emitted as graph attributes. They are now reserved in favor of the dedicated defaults: fields. Any workflow that previously set either key via vars: should move it into defaults:; otherwise the value is silently dropped from DOT output (the runtime would see no allowlist). This path was never documented — issue #28 filed specifically because defaults: rejected the keys — so the affected population is expected to be zero, but calling it out explicitly for anyone who found the workaround.manager_loop node kind for supervising a child sub-pipeline with polling and mid-run context steering. Maps to the runtime’s stack.manager_loop and DOT shape=house. Fields: subgraph_ref, poll_interval, max_cycles, stop_condition, steer_condition, steer_context (inline k=v,k=v or block form). Round-trips losslessly through parser → formatter → DOT export → migrate. Requires the parallel runtime adapter update. (#26, #27)manager_loop validation: missing/nonexistent subgraph_ref (DIP135), invalid control field — negative poll_interval or max_cycles (DIP136), unbounded supervision with no stop_condition and no max_cycles (DIP137 — the manager_loop analog of DIP104).stack.* namespace recognized by DIP120 so stop_condition and steer_condition can reference stack.child.cycles, stack.child.outcome, stack.child.status without namespace warnings.dippin scaffold manager_loop template emits a starter supervisor workflow.manager_loop node rule, highlights coverage, corpus test, committed generated parser (src/parser.c et al.), new just tree-sitter-generate / just tree-sitter-test recipes, and CI drift check so generated files can’t drift from grammar.js without being caught.manager_loop keyword, new field names, stack.* namespace recognition.steer_context block-form routing — a single-entry block-form steer_context (one k: v line under the indent) lexes without an embedded newline; the previous newline-based heuristic mis-routed it to the inline CSV handler. Replaced with a separator-position check (: before = means block, = before : means inline).HumanConfig.Timeout / TimeoutAction on human nodes. Pairs with edge labels like when: timeout for auto-advance semantics. Round-trips through parser, formatter, DOT export, and migrate. (#22)WorkflowDefaults budget fields: max_total_tokens, max_cost_cents, max_wall_time. Allow workflows to declare global budget caps consumed by the runtime. (#22)ctx.node.<id>.* now validates as a legitimate read pattern in DIP121/DIP122, eliminating lint false-positives for cross-node state access. (#23).well-known/agent-skills/index.json, .well-known/mcp/server-card.json, .well-known/api-catalog, robots.txt, and hosted skill.md. Lets coding agents auto-discover dippin-lang tooling. (#24)reasoning_effort expansion — DIP119 now accepts none, minimal, low, medium, high, xhigh, and max to cover Opus 4.7 and GPT-5.4.claude-opus-4-7 (Anthropic, $5/$25), mistral-small-2603 (Mistral Small 4), command-a-03-2025 (Cohere flagship, $2.50/$10).conditional node kind and vars section introduced in v0.20.0.claude-haiku-3-5 deprecation comment corrected — retired 2026-02-19, not 2026-04-19.vars block at the workflow level for declaring user-defined variables. Vars export as DOT graph-level attributes and round-trip through parse → format → export → migrate.max_retries is set in defaults with restart: true edges but no max_restarts — catches the common confusion between per-node LLM retries and loop restart budget.releasecheck/) — validates the embedded spec is tracked, current, and buildable from a source tree without .git.mvdan.cc/sh/v3. Variable assignments, command substitutions, and command -v checks are now correctly identified. Preamble commands (mkdir) are skipped to find the real tool binary.go install ...@latest broken — cmd/dippin/generated-spec.md is now checked into the repo so the go:embed directive resolves from module proxy downloads.just check now mirror CI exactly — spec freshness, release checks, complexity exclusions all aligned.working_dir field on agent nodes for per-node working directory override (e.g., .ai/worktrees/claude). Wired through parser, formatter, DOT export, and migrate.backend field on agent nodes for per-node backend selection (e.g., native, claude-code, acp). Previously this value was silently dropped by the parser.working_dir field on agent nodes for per-node working directory override.params:, instead of being silently discarded.flatten package for resolving subgraph refs into flat workflows. Recursive resolution with cycle detection and configurable depth limit (default 10). Underscore-prefixed node IDs (Parent_Child).dippin export-dip command — exports a flattened workflow as canonical .dip text with all subgraph refs inlined.dippin export-dot now automatically flattens subgraph refs before export, producing valid DOT without external references.orchestrator.dip (parent with subgraph ref) and phases/code_review.dip (child workflow).TestLintExamples now recurses into examples/*/ subdirectories.Start/Exit fields are now correctly remapped when they point to inlined subgraph nodes.flatten.Flatten returns a clear error instead of panicking when the resolver is nil but subgraph refs are present.export-dot error rendering — flatten errors now use renderError for JSON output consistency.conditional node kind for pure branching without LLM calls. Evaluates outgoing edge conditions only — no prompt, no token cost. Maps to diamond shape in DOT export. DOT migration auto-detects: bare diamond → conditional, diamond + prompt → agent, diamond + tool_command → tool.--extra-models CLI flag on lint and doctor commands. Extends the DIP108 model catalog at runtime for private or newly-released models. Format: --extra-models "provider:model1,model2;provider2:model3".[label: ...]) now emits a clear parse error with a hint to use when/label: keyword syntax, instead of silently discarding annotations.retry blocks now emit a clear parse error suggesting flat attributes (retry_policy, max_retries, retry_target, fallback_target, base_delay), instead of a confusing indent mismatch error.response_format: force LLM to produce structured JSON output (json_object or json_schema)response_schema: inline JSON Schema definition (multiline block, like prompt:)params: generic key-value pass-through for runtime features (same syntax as subgraph params)response_format value.response_schema is set without response_format: json_schema (schema ignored); hint when json_schema is set without a schema.response_schema is not valid JSON.params key shadows a first-class field (e.g., model, provider).cmd_timeout field now parsed and formatted on agent nodes (previously only populated by DOT migrator).AgentConfig.Params initialized to empty map (matching SubgraphConfig), preventing nil-pointer issues in downstream consumers.mode: interview). Runtimes extract questions from upstream agent output and present each as an individual form field with optional suggested answers. New fields: questions_key, answers_key.default value.api_design.dip example updated to use interview mode for Q&A collection.interview_loop.dip example: reusable interview subgraph with iterative Q&A. Parameterized by topic and focus areas. LLM generates questions with suggested options, human answers via interview mode, assessor loops until requirements are clear. Grade A, ~$0.92/run.site/ to GitHub Pages on successful main builds.--version / -version flags now work (previously failed with “flag provided but not defined”)."API design") were double-quoted on each format pass. Parser now strips surrounding quotes from param values.code_health_check.dip example: self-contained pipeline that audits a Go repo. Gathers context with shell tools, runs vet/staticcheck/tests in parallel, three-model independent review, synthesized report with quality gate and retry loop. 5 test scenarios. Grade A, ~$1/run.site/_layout/nav.html is the one source of truth. scripts/sync-nav.sh propagates it to all 16 pages with correct prefixes and active states. Pre-commit hook runs it automatically. No more editing nav in 16 files.scripts/gen-changelog-html.sh emits a placeholder nav that sync-nav.sh fills.just sync-nav recipe added.just install and just build now inject commit hash and build timestamp via ldflags. dippin version shows dev (commit: abc1234, built: 2026-03-27T18:45:10Z) instead of dev (commit: none, built: unknown).display: none)..tracker/field-report-response-2026-03-27.md).docs/blog-ideas.md) with 25 post synopses, coverage plans, and approach notes.<pre> overlay. Tab key inserts 2 spaces.[] not null for zero diagnostics.pre .hl-* to .hl-* so colors work in playground output div, not just <pre> blocks.compare-code divs (like gate.test.json on Testing page) now get highlighted — skip logic checks for existing <span> tags instead of parent class.\x00N\x00 placeholders prevents regex passes from matching inside previously generated <span> class attributes.$ dippin --format json test) gets JSON highlighting applied to the embedded body.scripts/gen-changelog-html.sh. Pre-commit hook runs it when CHANGELOG.md is staged.dippin.wasm, wasm_exec.js) now deployed to gh-pages so the playground actually loads.highlight.js) for all code blocks — Dippin, shell, terminal, and diagnostic output.changelog.html with full version history.ref: file validation — warns when referenced workflow file does not exist on disk.dippin watch command: file watcher that re-runs lint on .dip changes with 200ms debounce. Uses fsnotify.dippin test --coverage flag: edge coverage summary showing which workflow edges were/weren’t traversed by test scenarios.editors/tree-sitter-dippin/ — grammar.js, external scanner for indentation, highlight queries, and test corpus. Enables proper syntax highlighting in Neovim, Helix, and Zed.site/playground.html — browser-based editor with live parse, lint, and format via WebAssembly. Build with just wasm.gemini-3.1-pro-preview-customtools added to model catalog and pricing tables.errcheck on f.Close(), funlen on buildLintExplanations (split into 4 functions), misspell false positive in DIP118 example.consensus_task_parity.dip and semport_thematic.dip model names now match DOT originals (gemini-3.1-pro-preview-customtools).validator/lint_tool_cmd.go split with //go:build !wasm / wasm tags — bash -n syntax check and exec.LookPath binary check are no-ops in WASM.validator/lint_subgraph.go similarly gated for WASM (no os.Stat).docs/validation.md — full entry for DIP126.docs/cli.md — watch command section, test --coverage flag.docs/editor-setup.md — tree-sitter grammar availability.dippin explain DIP126 — explanation with trigger and fix guidance.mode: labeled documented as not supported in docs/nodes.md.bash -n.${ctx.*} references in tool commands that expand to empty at runtime.docs/validation.md — full entries for DIP123, DIP124, DIP125.dippin explain DIP123/DIP124/DIP125 — explanations with triggers and fix guidance.preferred_label now works on human gates — scenario key preferred_label (or per-node Gate.preferred_label) matches against edge labels (case-insensitive substring). Previously silently ignored on freeform gates.prompt: blocks now parse on human nodes — HumanConfig gained a Prompt field. Multiline prompt blocks work the same as on agent nodes. Formatter round-trips correctly."Node.tool_stdout": "") now suppress the auto-seeded success default, allowing unconditional fallback edges to fire.immediately_after test assertion — assert adjacency in the execution path: "immediately_after": {"NodeX": "NodeY"} checks that NodeY is the very next node after NodeX.branch field for targeted parallel testing — "branch": ["WorkerA"] limits which parallel fan-out branches are simulated. Without it, all branches are walked (new default)..test.json files for vulnerability_analyzer, consensus_task, code_quality_sweep, and sprint_exec (20 tests across 4 workflows).just cover now excludes untestable files (main.go, cmd_lsp.go) from coverage reports.docs/testing.md — added Caveats section (not_visited fragility with loop-breaking), Clearing Defaults section (empty-string technique), immediately_after field documentation.buildAdjacency() included restart (back) edges, creating cycles that prevented Kahn’s algorithm from assigning layers to downstream nodes. All nodes are now rendered. Affects both full and compact modes.when ctx.tool_stdout not contains all-done loops would spin to the 500-step limit. New MaxNodeVisits option forces the loop-exit edge after N visits. The test runner sets this to 3 by default.dippin test — NodeName.key=value scenarios now work reliably because the loop-breaking fix allows the simulation to reach the target node.LoadTestFile now rejects .test.json files with zero tests.cmd/dippin coverage: 44.9% → 79.3%.and/or conditions correctly fire per-variable.just release tag msg recipe for tagging releases.explain, unused, graph, test added to commands table.appendConnector dead branch — identical if/else branches collapsed.dippin test — scenario test runner for workflow assertions. Define .test.json files alongside .dip workflows with expected status, visited/not-visited nodes, and path ordering. Supports --verbose flag for path tracing and JSON output for CI integration.testrunner/ — loads .test.json suites, runs each case through the simulator with injected scenario values, checks assertions against results.docs/testing.md — documents the .test.json format and test runner usage.IO.Writes. Skips when writes are empty (advisory) or variable is a reserved runtime key (ctx.outcome, ctx.status, ctx.internal.*, graph.*, params.*).ToolConfig.Outputs. Only fires for tool nodes with explicitly declared outputs.dippin explain.dippin explain <DIPxxx> — rich explanations for all 34 diagnostic codes. Shows trigger conditions, fix guidance, and example snippets. Supports text and JSON output.dippin unused <file> — detects dead-branch nodes (reachable from start but no path to exit) and estimates wasted cost per run. Reuses coverage.Analyze() sink detection + cost.Analyze() for cost enrichment.dippin graph [--compact] <file> — terminal-rendered ASCII DAG visualization. Full mode renders box-drawing nodes with connectors; compact mode outputs single-line [A] → [B] → [C] format. JSON mode outputs layer structure.unused/, graph/, testrunner/validator/explanations.go with Explanation struct for all DIP codes.{success, fail} pairs. Handles done/more_questions, tasks_remain/all_done, and any custom value set.outputs field, removed undocumented numeric operators (<, >, <=, >= parsed but silently returned false)state.* namespace (not implemented), removed ctx.preferred_label (not in codebase), added == as = alias, added not contains infix syntax<, >, <=, >= from valid operators (never evaluated, silent false was a trap)CHANGELOG.md with retroactive history for all versionsdocs/CONTRIBUTING.md — documentation accuracy protocol with persona matrixCLAUDE.md — project conventions, gotchas, versioning policyTestLintExamples) — lints all examples through real parseroutputs field documented in nodes.md and README.mddippin cost, dippin coverage, dippin doctor, dippin optimize, dippin diff, dippin feedback, dippin lspvar not contains val)contains X + not contains X recognized as exhaustivedocs/analysis.md, docs/editor-setup.mdLint() now calls EnsureConditionsParsed() before running checkscontains X / not contains X as overlappinggemini-3-pro → gemini-3.1-pro-preview, removed nonexistent IDs<br> instead of <br/>dippin cost and dippin coverage commandsdippin check, dippin new commands with 5 scaffold templatesbase_delay field for retry overridedippin version command