Skip to content

Decisions

Decisions, projected from decision entities. Edit a decision entity and regenerate.

IdTitleSummary
Adopt TypeScript as the substrate language for the SDLC pluginAdopt TypeScript as the substrate language for the SDLC plugin- The SDLC plugin’s substrate (CLI, validators, dispatch scripts, orchestration tooling) is TypeScript, converted from Python via [[M-0002]]. - Markdown skills (SKILL.md prose Claude reads) stay markdown — this ADR is about the executable substrate, not the authoring surface. - Author preference + multi-driver ergonomics (typed agent abstractions across Claude Code, Codex, future agents) drive the language choice. - Bun is the runtime; the sdlc project doctor install-check probes runtime deps and tells consumers what is missing. - This ADR fixes the direction; the conversion mechanics were scoped to [[M-0002]] (closed/done). - Language choice is decoupled from dispatch/workflow library. DarkFactory was not adopted; the op substrate was built in-house per [[D-0007-deterministic-op-substrate]]. - Scope bounded by [[D-VSLI-distributed-work-runner-architecture]]: the engine is Rust; this ADR governs the op/entity substrate and the flowline authoring surface, which the engine invokes as child-process steps over the step protocol. Shared contract types are excluded — those are defined in Rust and generated to TypeScript.
Agent git writes are worktree-isolated off origin/main; the primary checkout is the human’sAgent git writes are worktree-isolated off origin/main; the primary checkout is the human’s- No agent process mutates the primary checkout’s working tree, index, or HEAD. Every agent git write happens in an ephemeral worktree created off origin/main and lands via push (or PR). The primary checkout belongs to the human.
Canonical harness model — one definition, many control planesCanonical harness model — one definition, many control planes- The agentic-harness surface is held as a single canonical, host-neutral model — capabilities, commands, hooks, MCP servers, permissions, and context documents — and exporters project it onto each host’s native shape. One definition drives Claude Code, Codex, Cursor, and Gemini; no target is privileged.
Dashboard application — client SPA, frontend framework, and build-into-pluginDashboard application — client SPA, frontend framework, and build-into-plugin- The status dashboard is the first application in the [[D-0012-monorepo-tooling]] apps/ tree. This decision fixes how that application is built, served, and shipped; D-0012 fixes the workspace it lives in. See Context. - Serving/shipping: a client-rendered SPA served by the existing Bun/Hono server (no SSR); the built SPA is committed and drift-gated inside the plugin (web-dist/); v1 targets four data sources. See Decision. - Frontend: Vite + Vue 3. A client-rendered Vue 3 SPA built with Vite. The two requirements driving the pick — and v1 success criteria — are: spin up the backend and frontend easily (one-command dev), and easily generate bindings for the frontend to use. See Options considered. - Dependency direction follows [[S-0008-apps-consume-substrate-through-published-surfaces]]; the committed-bundle drift gate reuses the --check pattern from [[D-0010-deterministic-site-assembly]]. See Consequences. - Partially superseded by [[D-0018-sdf-control-plane-app]]: the SPA and the frontend-framework choice are superseded for the operator app (apps/dashboard excised, [[T-QT7X]]); the serving model and JSON API remain live as SDF’s data plane.
Datamodel categorization — five layers (Product / Architecture / SDLC / Work / Planning-meta)Datamodel categorization — five layers (Product / Architecture / SDLC / Work / Planning-meta)- Entities are grouped into five layers — Product, Architecture, SDLC, Work, and Planning-meta — each answering a distinct question about the project (Decision). - Layered framing assigns each entity its purpose by position, so a reader infers role from neighborhood instead of from the type name alone (Why). - A fifth Planning-meta layer is added beyond the canonical four to house governance artifacts (Decisions, Standards, Principles) that otherwise have no home (Why). - Each layer reads as a chain — purpose, structure, verification, execution, governance — so the entity roster reveals the system’s shape before any specific type (How to read the layers). - This ADR fixes only the categorization; per-entity roster, schemas, lifecycles, and adoption order live elsewhere (Out of scope). - Boundary cases and the “Planning-meta” name itself remain open and are resolved per-entity or by future rename, not pre-committed here (Open questions).
Deterministic op substrate: entity-oriented library, op registry, generated adaptersDeterministic op substrate: entity-oriented library, op registry, generated adapters- The deterministic core becomes an entity-oriented TypeScript library (plugin/lib/): operations live with the entity they act on, generalizing the precedent already set by model/entities/<type>/migrations/. - Every deterministic operation is declared once in a single registry (defineOp). The registry — not the CLI — is the source of truth. - Adapters are generated from the registry: the sdlc <path…> CLI is the shipped front-door; MCP and HTTP servers are planned projections of the same ops. One op, many doors. - Composition is layered. A deterministic composition (a composite op or a multi-step workflow definition) stays in the substrate. An LLM-driven composition (a skill) lives outside it. The line between them is the only thing that forces an op boundary. - This retires [[S-0001-co-locate-first-promote-when-shared]] for capabilities (operations are placed with their entity by default, not promoted on a second caller) and amends [[S-0004-sdlc-cli-llm-head-deterministic-tail]] (the CLI is one adapter among several; the “head” is a spectrum, not always thin). - Supersedes the design in [[sdlc-cli]], which prototyped the noun/verb CLI and the head/tail split that this ADR generalizes. - Migration was taxonomy-first, then a staged big-bang (executed under milestone M-0003); the cost concentrated in re-cutting a handful of composite scripts at their judgment seams, not in the clean ports.
Distribution — CLI-primary npm artifact; harnesses are control planesDistribution — CLI-primary npm artifact; harnesses are control planes- sdlc is the primary interface to the app. Harnesses — the Claude Code plugin today — are control planes into it, not the ship vehicle. A head defers to the installed app’s version, never the reverse. See Decision. - One npm artifact, @sksizer/sdlc, carries both doors: the app (substrate + CLI, bin sdlc) and the Claude harness (the plugin directory as a package asset). Consumers pin one version via their lockfile; plugin/CLI skew is impossible by construction. - While private, distribution is a git-pinned dist repo — release CI pushes the built publishable tree; consumers pin a tag/sha as a git dependency, no registry auth. Public npm when it goes public. Shipped code is JS-runtime-agnostic and the artifact ships built JS (Node refuses to type-strip under node_modules), so npx sdlc needs nothing but Node ≥ 20. Bun stays the workspace toolchain ([[D-0012-monorepo-tooling]]); the handful of Bun.* calls in shipped code are named migration debt. - Harness targets will multiply. Skills head for a target-agnostic source form transformed into per-target control planes (Claude Code is the first target). Not scheduled here; recorded so distribution choices don’t foreclose it. - Onboarding is CLI verbs: bun add -d @sksizer/sdlcsdlc project setupsdlc harness install claude. - Live-source stays available as an opt-in mode for any consumer. The built artifact is the Bun-free default; a consumer may instead run the substrate from a source checkout under Bun (linked install or SDLC_HOME), the same way the dev repo dogfoods. See Decision. - Resolves [[D-0001-project-structure]]‘s deferred “substrate bridging mechanism” question: the bridge is a separate package install.
Entity definition architecture — common-denominator frontmatter and per-entity definition surfaceEntity definition architecture — common-denominator frontmatter and per-entity definition surface- Every entity is a directory under plugin/lib/model/entities/<type>/ carrying a fixed surface: schema.ts (the Zod frontmatter schema and the markdown-contract contract(...) body grammar), body-template.eta, definition.md, plus optional obsidian-index.yaml and ops/ (Entity file surface). - Frontmatter shared by every type is declared once as the CommonFrontmatter Zod base (_common.ts) and extended per type via .extend({ … }); defaults live as Zod .default()s. There is no separate frontmatter-defaults file (Common-denominator frontmatter). - The optional obsidian-index.yaml is an Obsidian Bases view template; /sdlc:setup --obsidian materializes it into a consuming project. Its filename is configurable via plugin/lib/configuration.ts (Obsidian Bases view template). - Operations are declared per entity in definition.md and implemented as defineOp descriptor modules under ops/, discovered by a runtime walk (Operations). - A plugin/lib/model/entity.ts projection assembles the surface into one object so the validator, scaffolder, and dispatcher share schema-loading and validation glue (Entity projection).
Entity identifier shape — AA-NNNN-Entity identifier shape — AA-NNNN-- Every entity filename is AA-NNNN-<optional-slug>.md — an uppercase type abbreviation, a 4-character id, and an optional, immutable slug. - The id is identity: immutable, globally unique within its prefix, and the stable anchor for wikilinks and git history. - Two numbering styles: incrementing for curated, low-volume catalogs (Standards, Principles); base-36 for high-volume, machine-minted, or branch-parallel records (Decisions, Milestones, Tasks, Backlog). - Filenames are immutable; the slug is authoring convenience and never tracks title changes. - Order lives outside the id — in a manifest, or the Milestone version.
Markdown formatting standard — Obsidian-first authoring choicesMarkdown formatting standard — Obsidian-first authoring choices- One Obsidian-first markdown rule set binds all docs and skill prose: ATX headings, - bullets, explicit numbering, inline links plus wikilinks, hand-formatted tables, language-tagged fences, no raw HTML, prose wrapped at 100 columns (see Formatting axes). - Rules split across three enforcement tracks — formatter, schema validator, reviewer (see Enforcement split). - [[S-0007-markdown-formatting]] codifies the binding rule set; [[D-0011-markdown-formatting-tool]] picked the enforcing formatter (rumdl); [[T-0008]] wired it and ran the one-time corpus reformat.
Markdown formatting tool — Obsidian-safe formatter choiceMarkdown formatting tool — Obsidian-safe formatter choice- rumdl (Rust linter-formatter, ruff model) is the markdown formatter — the only surveyed tool whose table formatting (MD060) is opt-in and off by default, satisfying the Standard’s hand-formatted-tables rule. - One tool covers the whole formatter track: MD013 reflow = true wraps prose at 100 columns and the MD040 fix tags bare fences text — no custom wrap script needed. - Prettier and dprint stay disqualified (unavoidable table column-padding); the markdownlint-cli2 + wrap-script hybrid is the specified fallback; dprint a tertiary watch. - Empirically verified: byte-identical fixture round-trip; forensic full-corpus pass (660 files) with wikilink/embed multiset, tables, and frontmatter byte-identical; idempotent second pass. - Accepted caveats: two narrow reflow bugs (embed-glue — zero corpus instances; anchor displacement — three lines hand-fixed before the reformat), tracked for upstream filing via B-1ATA. - [[T-0008]] wired it (PR #356): pinned bunx rumdl@0.2.9 in the lefthook markdown-fmt step, justfile recipes, and the /markdown-format skill; check + fixture-pin quality gates; and the one-time corpus reformat (~7,000 fixes across 433 files).
Monorepo structure and workspace tooling — polyglot apps/packages on moonMonorepo structure and workspace tooling — polyglot apps/packages on moon- A frontend application consuming the substrate fires [[D-0001-project-structure]]‘s “additional tool consumes substrate” transition trigger. This decision fixes how the resulting monorepo is structured and tooled; [[D-0001-project-structure]] fixes where its trees live. See Context. - The repo is polyglot — at least two ecosystems (TypeScript and Rust, n ≥ 2). The dashboard is TypeScript; the planned Tauri desktop app brings Rust. This assumption drives every choice below. See Why. - Settled: an apps/ + packages/<ecosystem>/ workspace; moon as the workspace runner; the JS/TS ecosystem unified on Bun as package manager. See Decision. - How each application builds and ships (e.g. into the plugin) is decided in that application’s own decision, not here. See Notes.
Op-substrate surface: path[] command paths, umbrella nouns, defineService, render hookOp-substrate surface: path[] command paths, umbrella nouns, defineService, render hook- A command is a 2–3 segment kebab path (path: string[], hard cap 3), replacing the flat {noun, verb} descriptor. Depth-3 only where a group has ≥2 members; singleton groups are banned. - The surface lands at 11 visible nouns (task backlog milestone standard entities commit report docs quality project dashboard) and 4 hidden (plugin pr gate lease); nouns added by later decisions (config, orchestrate, reference, term) follow the same rules. Entity CRUD is untouched; the 18 remaining script capabilities get final homes (notably task inflight, project scan {todos,candidates}, quality baseline {capture,diff,prune}). - defineService is a registry sibling to defineOp for long-running capabilities (dashboard, lease heartbeat-loop): same path-keyed index and help surface, lifecycle dispatch (start/stop?/list?) instead of request/response. Resolves D-0007’s open question. - One output contract (§4): every op’s canonical result is its zod output object; a single --output text|json|jsonl parameter shapes the projection over two op classes (sync, streaming). Defaults are op-declared: a descriptor-supplied cli.render(output, io) hook is the text projection and makes text the default (parity for line-oriented consumers like task next and quality run --log); without one, json — today’s adapter behavior. - Adapter projection is a pure function of the path: CLI segments verbatim; MCP tool name = underscore-join (forward-only, uniqueness enforced by project-check, not reversibility); HTTP route = /<seg>/<seg>[/<seg>] with method by verb class. - This amends [[D-0007-deterministic-op-substrate]] (§2 descriptor shape) and resolves two of its open questions (long-running ops; skill-local triage). Migration sequencing lives in the op-substrate full-sweep milestone.
Project structure — evolutionary; substrate at apps/sdlc/lib/ today, the ontological solution as end-stateProject structure — evolutionary; substrate at apps/sdlc/lib/ today, the ontological solution as end-state- Project structure is evolutionary: the SDLC/ontological product — substrate, protocol adapters, harness surfaces, and its applications — lives under apps/sdlc/ today and merges into solutions/ontological/ at end-state: one solution-local cluster whose consumers reach it only through published surfaces. See Decision and End-state target. - Vocabulary. An adapter is a protocol surface over the substrate (CLI, MCP, HTTP); a harness is an agent runner that consumes the substrate (Claude Code, Gemini); an application is a deployable expression of SDLC for people (web dashboard, desktop app). A harness is conceptually app-like; an application may bundle and deploy harnesses within itself. - The repo is polyglot — at least two ecosystems (TypeScript and Rust, n ≥ 2). The dashboard is TypeScript; a planned Tauri app brings Rust. This drives the workspace tooling in [[D-0012-monorepo-tooling]]. - Substrate lives at apps/sdlc/lib/ today, headed for the solution’s substrate subtree. No top-level src/ tree: consumption through published surfaces (compiled binary / npm externally, launcher shim / dev-link locally) keeps the source solution-local. See Current state. - apps/<name>/ holds deployable applications (each possibly polyglot internally); packages/<ecosystem>/ holds shared libraries partitioned by language. Tooling by [[D-0012-monorepo-tooling]]; dependency direction by [[S-0008-apps-consume-substrate-through-published-surfaces]]. - A solution (solutions/<name>/) is a cluster of apps and packages that belong to one solution and are shared only within it — typically a nested {apps/, packages/{ts,rust}}. Its sharing scope is narrower than repo-wide packages/ and wider than a single app. The tell: a solution carries multiple deployables (CLI, desktop, web, workers) sharing solution-local packages; an app is a single deployable, however polyglot inside. Placement follows sharing scope, and a package earns a wider home by observed use, not by up-front guess. See Placement and promotion. - The move is decided (2026-08-14) and executes as one dedicated mechanical PR in a freeze window — after the in-flight apps/sdlc PRs land and orchestrator dispatch is paused. See Transition rule. - apps/sdlc/lib/ internals and the registry/adapter-generation model are fixed by [[D-0007-deterministic-op-substrate]], which refines this ADR. - sdlc.yaml and similar repo-level configs live at the repo root intentionally: SDLC’s configuration should be visible at top level. See Resolved questions.
Reference entity type, generated references roster, and the docs AppendixReference entity type, generated references roster, and the docs Appendix- A new reference entity type (prefix RF, incrementing ids, docs/planning/references/) catalogs external documentation, research, and artifacts as first-class entities: an optional url in frontmatter, a transcludable Summary, and a Material section that may summarize, copy, or transclude the source. (§1, §2) - docs/references.md is a new build artifact — the references roster, generated from reference entities beside index.md and glossary.md. The docs generate artifact list grows to index | glossary | references | site. (§3) - Terms and references move into the Appendix: index.md’s existing # Appendix gains generated Glossary and References sections, and the docs site’s sidebar gains a trailing Appendix group holding the Glossary and References pages. (§4) - The site’s Architecture → Data model page becomes generated: its identity and roster tables derive from the PREFIXES registry and the entity schemas in the same docs generate site pass, so the page can never again claim “nine entity types” after the tenth ships. (§5) - The type is named reference, not evidence — [[D-ORMG-data-model]]‘s deferred Evidence is a high-volume per-claim support record; Reference is a curated, low-volume source catalog. Evidence stays deferred. (Options considered) - Migration is the dogfood: the external sources SDLC already leans on (Obsidian Bases, Eta, Starlight, Bun, lefthook, JSON Schema, the ADR lineage, …) are minted as the seed corpus. (Migration)
SDLC distribution — standalone apps/sdlc/ app, compiled bun binarySDLC distribution — standalone apps/sdlc/ app, compiled bun binary
Suspenders — skill authoring as an app-frame experience moduleSuspenders — skill authoring as an app-frame experience module- Skill browsing, authoring and testing ships as suspenders, an app-frame experience module — not a standalone application. One module, two hosts: the SDLC desktop control plane and a thin standalone product.
System architecture — the distributed work runnerSystem architecture — the distributed work runner- SDLC is a distributed work runner: work orders are scheduled, claimed, and executed by registered processes; the SDLC entities and flows are the primary registered instance, not the system itself.
Task pickup order — sdlc task nextTask pickup order — sdlc task next- Pickup order is owned by one deterministic verb, sdlc task next (plugin/lib/model/entities/task/ops/next.ts); both /sdlc:orchestrate and the no-arg /sdlc:task-work shell out to it instead of carrying duplicate prose.
Task state plane split — semantic corpus, execution substrateTask state plane split — semantic corpus, execution substrate- Task conflates two roles: the semantic work item (corpus) and the execution record (runtime). The schema already declares the doctrine — status “is a workflow cache — the lease ref at refs/sdlc/tasks/<id> is the authoritative claim” — but nothing enforces it: every run lands ~4 cache commits on main. - This decision finishes the split by storage plane, not by minting a second entity type. Frontmatter holds semantic state; the lease payload holds execution state. - Four semantic transitions stay as commits on main: creation, definition movement within planning/*, promotion to open/ready, and closure. After promotion, a task’s file changes on main exactly once more — at closure. - readiness_verified_at is written once, ON the promotion commit — promotion IS the readiness claim. Per-run re-verification writes a gates object in the lease payload and never commits to main. - in-progress[/blocked] become lease phases only. The enum values stay tolerated-but-never-written through a deprecation window (schema v6 deprecates, v7 removes). - Migration is staged as seven tasks, expand → cutover → contract, each leaving the live orchestrator operable.
Term entity type, generated glossary, and the docs generate surfaceTerm entity type, generated glossary, and the docs generate surface- A new term entity type (prefix TM, incrementing ids, docs/planning/terms/) makes each glossary entry a first-class entity: schema, body manifest, template, sdlc term create, and automatic audit/validate/migrate/setup coverage. (§1, §2) - glossary.md becomes a build artifact — generated from term entities, never hand-edited — and moves from docs/planning/glossary.md to docs/glossary.md, beside the other generated front-door, index.md. (§3) - The generator surface becomes sdlc docs generate [<artifact>…] — one op, one deterministic pass over the generated-docs family: the wiki index (docs/index.md), the glossary (docs/glossary.md), and the docs site’s generated pages. Artifacts select positionally for scoped runs (sdlc docs generate glossary); bare invocation regenerates the committed wiki artifacts, with site named explicitly ([[D-0010-deterministic-site-assembly]]). Subsumes index generate; amends the [[D-H7FS-op-substrate-surface]] noun table (indexdocs). (§4) - The glossary ships into the docs site as a generated page with definitions inlined from term entities — and site generation moves onto the substrate: site/scripts/regen.mjs ports into the docs service as first-class lib code and is deleted. (§4, Migration) - A term’s Definition section carries the ^summary block; the glossary table transcludes it, so the table never drifts from the entity — the mechanism index.md already uses. (§2) - The type is named term, not definition — “definition” already names the per-entity companion doc (definition.md, S-0005), and the D prefix is taken. Supersedes the working name in [[B-T2DB-definition-entity-for-glossary]]. (Options considered) - Migration is the dogfood: ~13 instances minted from today’s glossary rows, the hand-written file deleted, deep links re-pointed at term entities, D-0002/D-ORMG prefix registries amended. (Migration)
The Determined design languageThe Determined design language- The suite-wide design language is the one packages/ts/ui (@determined/ui) already implements — the --d-* token system, the dSemantics intent contract, and the D* component kit. It is named Determined. No new system is built; this one is promoted, governed, and extended outward by descent.

Emerging / retired:

IdTitleSummaryStatus
Contract authoring and runtime ownershipContract authoring and runtime ownership- The contract vocabulary (mcVersion-2 meta-schema) and the Finding DTO are cross-language normative now; which engine runs them is decided per surface, not globally.open/proposed
Deterministic site assembly: generated nav, supplemental content mapping, planning-corpus projectionsDeterministic site assembly: generated nav, supplemental content mapping, planning-corpus projections- The docs site is a pure build artifact: one sdlc docs generate site pass owns every file under <docs_site>/src/content/docs/ and the sidebar. A file the pass did not place is either a stale generated page (deleted loudly) or foreign hand-written content (hard error). (§1) - Nothing generated is committed: the Astro scaffold ships at plugin/site-template/ and materializes into <docs_site>/ (located by sdlc.yaml’s docs_site key); a consumer commits only site.yaml and supplemental/ — Cloudflare rebuilds the rest on every deploy. (§1) - Supplemental hand-written pages live outside the content root in <docs_site>/supplemental/, mapped in through a declarative manifest (<docs_site>/site.yaml) that places each page or group in the nav — top level, or two levels at most. (§2) - Naming collisions fail the build: a single route registry records every placement; duplicate routes, supplemental pages inside generated namespaces, and duplicate nav labels exit NAMESPACE_CONFLICT. (§2) - The sidebar is generated: site.yaml’s ordered nav slots expand into a generated module that astro.config.mjs imports — the plugin-owned config stays a thin shell. (§3) - Every planning entity type projects onto the site: a generic projection (roster + per-entity child pages, the [[D-0009-reference-entity-and-docs-appendix]] P6 pattern generalized) renders any type with zero wiring; per-type modules override where the generic shape isn’t right. Glossary and References re-platform as the first two overrides. (§4) - The Roadmap page becomes generated from milestone entities — static narrative preamble plus Active/Emerging tables, the data-model-page precedent. (§5) - Site pages render through Eta templates, one per page family, matching the index/glossary/references artifact generators and the report-kind pattern. (§6)open/proposed
Final data model — target entity ontology and rosterFinal data model — target entity ontology and roster- The SDLC data model is a five-layer entity ontology — Product / Architecture / SDLC / Work / Planning-meta — categorized in [[D-0003-datamodel-categorization]]. - Every artifact with a schema that validates is an entity; the definition contract is [[S-0005-entity-definition-contract]] and the definition architecture is [[D-0004-entity-definition-architecture]]. - This document carries the authoritative roster below: every entity type, its status, prefix, and purpose. The filename shape itself is fixed by [[D-0002-entity-identifier-shape]]. - Eleven entities are active today, all on the S0005 surface — Decision, Standard, Principle, Term, Reference, Milestone, Task, Backlog, Capability, Driver, Product; the rest are planned or deferred (Epic is retired into Task parent_key). - This is the roster and the target picture — not a re-derivation of the layer rationale (that lives in [[D-0003-datamodel-categorization]]).open/proposed
Markdown dialect and compatibility profileMarkdown dialect and compatibility profile- One normative dialect and compatibility profile for the markdown ecosystem: the construct set, malformed-input policy, position model, and document framing (BOM, line endings). The profile — not any tree materialization — is what implementations must agree on.open/proposed
Ontogen as shared codegen substrate — evaluate convergence with the zod contract clientOntogen as shared codegen substrate — evaluate convergence with the zod contract client- Pumice ([[D-0016-pumice-app-integration]]) brings ontogen — a Rust code-generation pipeline (rust-ontogen, already an external crate) that emits a dual HTTP + Tauri-IPC transport plus a typed TS client from one Rust API definition. See Context. - This monorepo independently solves an adjacent problem: it generates packages/ts/dashboard-client from a zod contract via gen-client.ts ([[D-0013-dashboard-app]], [[T-JZL4]]). Two apps, two “typed client from a schema” pipelines. See Context. - The question: should the workspace standardize on ontogen as the shared codegen substrate, keep the two pipelines separate, or extract a common contract seam? This ADR lays out the options and a recommendation to evaluate, gated on the pumice co-tenant landing. See Decision. - Ontogen’s dual HTTP+IPC transport also directly answers [[T-CKC1]]‘s open “sidecar-HTTP vs Tauri invoke” question — pumice already ships both from one definition. See Why.open/proposed
Pumice desktop app integration — vendor as a co-tenant, converge to sharedPumice desktop app integration — vendor as a co-tenant, converge to shared- Pumice — an existing standalone Tauri v2 + Nuxt 4 desktop application with its own Rust crates and code-generation pipeline — is brought into this monorepo as apps/pumice, fulfilling [[D-0012-monorepo-tooling]]‘s polyglot brief with a real, load-bearing second ecosystem rather than a thin validating wrapper. See Context. - The integration is phased. Short term, pumice lands as a self-contained co-tenant: it shares the monorepo’s tooling substrate (moon graph, CI, justfile, lefthook) but no application code. Long term, it converges — shared Rust crates promote to packages/rust/, and any substrate consumption goes through the [[S-0008-apps-consume-substrate-through-published-surfaces]] adapter seam. See Decision. - The JS/TS ecosystem unifies on Bun, per [[D-0012-monorepo-tooling]]. Pumice’s frontend migrates from pnpm to Bun as part of onboarding; the three pnpm lockfiles are retired. See Decision. - Pumice is a fat app (its own frontend, domain crates, ontogen codegen, template-sync, release pipeline), which distinguishes it from the thin Tauri wrapper drafted in [[T-CKC1]]. Their relationship is resolved in Notes. See Why.open/proposed
Pure functional YAML field editing over bytesPure functional YAML field editing over bytes- yaml-splice is a pure, deterministic library: UTF-8 YAML bytes plus an ordered list of top-level field edits produce new bytes or a structured refusal. It has no filesystem, markdown, schema, clock, locale, or process dependencies. - A semantic no-op is a textual no-op. Changing a field replaces only that field’s authored span; every non-target byte remains identical. New material is rendered deterministically without reserializing the document. - V1 deliberately edits one YAML document whose root is a string-keyed mapping. Nested paths ([[#^nested-path]]), raw-fragment injection ([[#^raw-fragment]]), multi-document streams ([[#^yaml-stream]]), formatting, frontmatter fences, validation, CAS, and atomic writes stay outside the library.open/proposed
Repair-on-prepare and aggressive entity upgrade — self-healing posture vs strict downshiftRepair-on-prepare and aggressive entity upgrade — self-healing posture vs strict downshift- Current posture is mostly detect-and-defer: task-ensure-ready analyzes a task spec, flags drift from the readiness contract, downshifts to planning/needs-definition with a definition_gap field, and exits. The one repair path today is the autonomy gate: autonomy: autonomous/pr tasks get a single /sdlc:task-auto-define fill-and-re-verify pass before any downshift. /sdlc:entities-migrate applies deterministic-mechanical fixes only (schema_version stamps plus the versioned body transforms); non-mechanical drift surfaces and waits for human attention. - Proposed posture is active-repair-with-confidence-gate: both surfaces gain an LLM-orchestrated repair pass that attempts to fix the drift inline. Mechanical fixes ship without ceremony; non-mechanical fixes either ship if the LLM’s confidence clears a gate, or fall back to today’s downshift/PR-for-review behavior. - Three concrete surfaces affected: (1) task-ensure-ready (or a new task-prepare step) gains a repair mode that fixes spec drift before downshifting; (2) /sdlc:entities-migrate extends to handle non-mechanical upgrades via LLM orchestration; (3) /sdlc:task-work invokes the repair-mode prepare step so a single dispatch can repair-and-continue without an out-of-band cycle. - This ADR sets the posture and the confidence-gate principle. Specific repair recipes, gate thresholds, and skill-or-flag shape are downstream tasks.open/proposed
SDF control-plane app — Tauri+Nuxt operator surfaceSDF control-plane app — Tauri+Nuxt operator surface- apps/sdf is the DarkFactory operator surface: the dashboard/observer component [[D-VSLI-distributed-work-runner-architecture]] reserves, built as a Tauri 2 + Nuxt 4 app with a first-class browser mode, scaffolded from template-tauri-nuxt by replaying the proven pumice recipe ([[D-0016-pumice-app-integration]]). See Context. - It supersedes the dashboard SPA and the thin wrapper. apps/dashboard (the [[D-0013-dashboard-app]] Vite+Vue SPA) and [[T-CKC1]]‘s drafted apps/desktop are excised aggressively once SDF’s active-work view lands — good ideas are adopted, the code is removed. apps/pumice and apps/legacy-polish are peers, not part of SDF. See Decision. - Contract: ontogen-first, reconciled with the substrate schemas. SDF’s own Rust↔TS contract is generated by ontogen (HTTP + Tauri IPC + MCP transports, typed TS client, drift-gated) — the concrete convergence event [[D-0017-ontogen-shared-codegen-convergence]] deferred to. Substrate entities stay owned by the SDLC zod/markdown-contract schemas; a parity gate keeps SDF’s mirrored read-model aligned. See Decision. - Observe first, control later. Early phases are read-only over existing data planes (GET /api/state, lease refs, .sdlc/ logs). Bounded operator actions arrive in a later phase behind the platform-core token pattern; the human merge gate is untouched. See Migration / rollout.open/proposed
Skill shape — required SKILL.md structure and post-execution assessmentSkill shape — required SKILL.md structure and post-execution assessment- Pending — exploratory ADR; outcomes populate here when it reaches open/accepted. - Proposes a workflow contract for every SKILL.md: required body sections (Trigger / Project context / Steps / Outputs / Post-execution assessment / Failure modes). - Load-bearing piece: a required post-execution assessment step so skill behavior compounds into improvement instead of evaporating per run.open/proposed
The why → what → verify chain — Product / Driver / Goal / Requirement / AcceptanceCriterionThe why → what → verify chain — Product / Driver / Goal / Requirement / AcceptanceCriterion- Designs the five entities around SDLC’s traceability chain: Product (the why), Capability (the what, already shipped), and Requirement / AcceptanceCriterion (the verify) form the chain; Driver is a standalone supplemental why-record; Goal, Requirement, and AcceptanceCriterion are deferred. - Every unit of work should trace up to a motivation and down to a test: Task → Requirement → Capability → Product, with AcceptanceCriterion proving the Requirement. - References point up. Each entity names its parent in the chain; containers derive the downward view via tooling — the same shape as Capability’s parent_key / contains. - Design only. This fixes the entities’ purpose, fields-in-prose, status lifecycles, and relationship cardinality. Schemas ship per entity when a milestone scaffolds them: Product and Driver are active in [[D-ORMG-data-model]]; Goal, Requirement, and AcceptanceCriterion are deferred. - Prefixes are fixed by the [[D-ORMG-data-model]] roster (PR, DR, G, R, AC); Specification stays deferred (the Capability body is the spec).open/proposed
Vault-transform safety protocolVault-transform safety protocol- The safety rules a vault reshape must satisfy before it may delete or overwrite output: a hard deny boundary distinct from seed selection, target-state preconditions with journaled recovery, collision keys computed before plan approval, and an explicit plugin trust model.open/proposed
yaml-splice WASM/TypeScript binding structureyaml-splice WASM/TypeScript binding structure- The pure yaml-splice crate ([[D-7N7I-pure-functional-yaml-editing]]) is Rust; its first real callers on the TypeScript side are the SDLC frontmatter writers that today reserialize whole blocks. Expose the crate to TypeScript over WebAssembly so those callers get byte-identical edit_yaml semantics from one implementation, not a second. - A thin packages/rust/yaml-splice-wasm cdylib owns the wasm-bindgen surface; packages/ts/yaml-splice (@sksizer/yaml-splice) ships the built artifact plus a hand-written typed façade. - The conformance corpus becomes language-neutral JSON that both the Rust crate and the TS package run — byte-parity is the contract, so the implementation (parser included) can change under either binding without breaking callers.open/proposed