Replies: 1 comment
Plan Update — August 29, 2026Architectural Decision: vibe-check as an Independent ProjectAfter implementing the universal coupling model (Group 10-architecture), we've made a significant architectural decision: vibe-check is an independent project, not part of gaze.
This means all RFC references to What's ShippedGroup 10 — Universal Multi-Language Coupling Model: COMPLETE ✅ PR #17 merged. This implements the entire Layer 1 foundation:
All six design decisions from the spec were implemented faithfully: flat Review council passed (9/9 agents approved after one fix iteration). OpenSpec change artifacts archived. Updated RoadmapThe issue tracker in zero-dot-force/vibe-check has been fully realigned. Here's the current plan: Phase 0: Foundation (COMPLETE)
Phase 1: Go Adapter + Enforcement
Phase 2: Accessibility + CI Gates
Phase 3: Multi-Language
Unchanged (scoped to gaze, not vibe-check)
Docs + Content
Deployment Patternvibe-check follows the same Assets live in Discussion Questions AnsweredFrom the original RFC:
Answered: Cross-language from the start. The universal model (Layer 1) shipped first as pure types and interfaces with zero language-specific assumptions. The Go adapter (Layer 2) comes next. The ExternalAdapter JSON-RPC protocol is already implemented and tested — rattler (Python) and future TS/JS adapters plug in without changes to the core.
Answered: Neither — build in vibe-check as an independent project. Same ecosystem philosophy as gaze but separate domain (design quality vs. test quality). No external dependencies. Next StepIssue #2 — |
Uh oh!
There was an error while loading. Please reload this page.
Valentina Servile's article "Should we still design code for humans?" (Thoughtworks, July 23, 2026) argues that good software design remains essential — and becomes more important — as AI agents write more code. Unbound Force is itself an AI agent system that works on other people's codebases, which makes this directly actionable for us. This RFC analyzes the article's thesis, audits our current capabilities against it, and proposes concrete additions.
TL;DR — We have strong function-level quality (Gaze CRAP, contract coverage, 37 side-effect types) and strong process governance (Review Council, constitution, speckit). What we almost entirely lack is structural/architectural quality measurement — the metrics that tell you whether a codebase is well-designed, whether it's improving or degrading, and where design debt is accumulating. Two P0 proposals:
gaze coupling) — Ca, Ce, Instability, Abstractness, Distance, cohesion, circular dependency detection. No single OSS tool computes the full Martin suite for Go today (re-confirmed August 14, 2026 — Gaze has exactly 4 commands:analyze,crap,quality,report).divisor-entropy.md) — a new Divisor council member that computes structural delta between base branch and PR branch. Because/uf.review-councildynamically discovers alldivisor-*agents at runtime, creating this file makes it a required reviewer with zero command changes. Low effort, high impact.Cross-repo scope: coupling metrics land in gaze; entropy agent + convention pack +
/metricscommand land in unbound-force (meta); drift tracking integrates with dewey.Next steps: RFC → consensus on priorities/thresholds → per-hero OpenSpec or Speckit specs → implement. See Discussion Questions at the end.
1. The Article's Thesis
Servile presents three core arguments under the umbrella claim that specs-as-abstraction-layer is a false analogy:
Specs are not the next abstraction layer. Unlike compilers (deterministic), agents are non-deterministic — the same spec produces different code each run. And specs precise enough to eliminate ambiguity start resembling code written in a worse language. Servile quotes Dijkstra (1978): formal symbols rule out nonsense that natural language cannot.
Good design matters to agents too. Agents trained on well-structured human code perform better on well-structured codebases. Poorly designed codebases — tangled coupling, inconsistent naming, unclear boundaries — cause agents to make more mistakes, consume more context, and cost more tokens. Technical debt is now literally quantifiable via the AI bill.
Agentic code degrades faster than human code, and humans must stay at code level. Agents violate the Boy Scout rule constantly, accumulating entropy at machine speed. Design decisions require inspecting actual code — "the code is where coupling, cohesion, duplication and complexity become visible." The audience for good code has expanded from humans only to humans and agents.
What This Means for Unbound Force
We're uniquely positioned: an AI agent system that should measure, track, and actively improve the structural quality of target codebases — not just detect bugs or test coverage. The article's thesis maps directly to a product opportunity.
2. Current State Assessment
Snapshot as of August 14, 2026. Re-verified against live repos.
2.1 What Already Exists
divisor-*agents — currently 9, with 6 code/spec reviewers: Guard, Architect, Adversary, Testing, SRE, Curator), constitution, speckit 8-phase pipeline (constitution → specify → clarify → plan → tasks → analyze → checklist → implement)go test -coverprofile, aligned with JaCoCo, SonarQube, and the original CRAP formula.2.2 What is Missing
3. Robert C. Martin's Package Metrics: Translation to Go
3.1 Background
Martin's package-level OO design metrics evaluate dependency management and structural quality. They were designed for class-based OO languages but translate well to Go's package system — Go packages are the architectural unit in the same way Java packages are.
3.2 Metric-by-Metric Translation
Metrics That Apply Directly (No Adaptation Needed)
Metrics That Require Adaptation
Go Abstractness note: Go interfaces are implicit (a type implements an interface without declaring it). A package full of concrete types used through interfaces defined elsewhere still serves an abstracting role even though its own A score is 0. A supplementary metric — Interface Stability (how many external packages depend on this package's interfaces specifically) — provides a more accurate picture.
Function-Level Equivalents (Fan-in / Fan-out)
Additional Structural Metrics
3.3 Python Applicability
3.4 Existing Tooling Landscape
Key finding: No single open-source tool computes the full Martin suite (Ca, Ce, I, A, D) for Go. This is a capability gap that Gaze can fill.
4. Recommended Additions
4.1 Group 1: Package-Level Structural Metrics in Gaze
The biggest gap. Gaze is entirely function-level. There is no package-level analysis anywhere in the system.
What to add — new
gaze coupling(orgaze graph) subcommand:Implementation approach (Option C):
golang.org/x/tools/go/packagesfor import graph andgo/callgraphfor function-level analysismetrics-analystagent that interprets results, stores in Dewey, creates work items for violationspackagessection alongside the existingfunctionssectionOutput example:
{ "packages": [ { "path": "internal/payment", "ca": 3, "ce": 12, "instability": 0.80, "abstractness": 0.15, "distance": 0.05, "cohesion": 0.42, "circular_deps": [], "zone": "pain", "status": "violation", "violations": [ "Instability 0.80 exceeds threshold 0.67", "Cohesion 0.42 below threshold 0.60" ] } ] }CI gate flags:
--max-instability=0.67— fail if any package exceeds threshold--max-distance=0.3— fail if any package is far from main sequence--no-circular-deps— fail on any import cycle--min-cohesion=0.5— fail on god packagesPriority: P0 — High effort, very high impact. Closes the biggest architectural blind spot.
Language scope note: Gaze currently analyzes Go only, but its side-effect taxonomy already has a universal multi-language design (issue #96, now closed). Section 4.10 proposes that
gaze couplingfollow the same two-layer architecture (universal metrics + language adapters via the external analyzer protocol) to support Go, Python, TypeScript, and JavaScript. See Section 4.10 for the full multi-language architecture and Discussion Questions for open design decisions.4.2 Group 2: Boy Scout Enforcement
The article argues agents violate the Boy Scout rule constantly. Unbound Force needs to actively prevent this.
4.2.1 Entropy Sentinel Agent
New Divisor council member:
divisor-entropy.mdRole: Computes structural delta between base branch and PR branch. Flags when:
Review Council integration: Because
/uf.review-councildynamically discovers alldivisor-*agents, creatingdivisor-entropy.mdautomatically adds it as a required reviewer — no command changes needed. This is the same mechanism that onboarded all 9 existing Divisor agents (Guard, Architect, Adversary, Testing, SRE, Curator, Scribe, Herald, Envoy). The agent specifically evaluates whether the PR improved or degraded the structural quality of the codebase.Priority: P0 — Low effort, high impact. Can use
godafor dependency analysis initially, then switch togaze couplingwhen Group 1 ships.4.2.2 Pre-Change CRAP Gate
Before an agent modifies a file, compute CRAP baseline; after modification, fail if any function's CRAP increased.
Implementation: New Gaze flag
--gate-on-changethat takes a baseline file and current analysis and outputs only functions where CRAP worsened.Priority: P1.
4.2.3 CRAP Delta in Review Council
Extend
gaze-reporter.mdto include a "Change Impact" section in full-mode reports that compares baseline metrics to current metrics. Currently the reporter shows absolute values only — it has no delta/change-impact reporting across commits.Priority: P1.
4.2.4 Package Metric Regression in CI
Like the existing
--max-craploadgate, add threshold enforcement on coupling metrics to CI workflows.Priority: P1 — depends on Group 1 being built first.
4.3 Group 3: Agent-Design Convention Pack
A new convention pack:
.opencode/uf/packs/agent-design.mdThis documents explicit conventions for writing code that is navigable by AI agents — the article's argument made actionable. Rules would include:
This pack is enforced by the
divisor-entropy.mdagent in the Review Council.Priority: P1 — Very low effort, high conceptual value.
4.4 Group 4:
/metricsCommand andmetrics-analystAgentNew command:
.opencode/commands/metrics.mdThree modes:
/metrics ./...— summary metrics table (coupling, cohesion, instability per package)/metrics detailed ./internal/payment— package-level breakdown with violations and recommendations/metrics trending ./...— historical trend analysis (30/60/90-day trajectory)New agent:
.opencode/agents/metrics-analyst.mdRole:
gaze coupling --format=jsonon target packagesdewey_store_learningfor trend trackingOutput format example:
Priority: P1 — Low effort, high value. Makes metrics accessible.
4.5 Group 5: Mutation Testing
Reassessment: Mutation testing was initially rated P1 but has been downgraded to P3/optional after analysing its overlap with Gaze's contract coverage.
Why it's largely redundant: Gaze's contract coverage answers "do my tests assert on observable behaviour?" through a four-pass static SSA assertion-to-effect mapping pipeline (Direct identity → Helper bridge → Indirect root → Inline call), plus an optional fifth AI fallback pass for ambiguous cases. Mutation testing answers a closely related question — "would my tests fail if I changed the code?" — but through brute-force code modification rather than static analysis. For codebases already using GazeCRAP with contract coverage gates, the incremental signal from mutation testing is narrow.
Residual value: Mutation testing catches one class of problem that contract coverage cannot: weak assertion patterns where a test references a side effect but the assertion itself is too permissive to fail (e.g.,
assert result != nilwhen the result is always non-nil regardless of correctness, or logging a value instead of asserting on it). This is a real but narrow gap.Contract coverage's own limitation: Gaze confirms assertions reference side effects, not that assertions would fail if the side effect was removed or changed. However, in practice, an assertion that references the return value of a function is overwhelmingly likely to fail if that return value changes — the gap is theoretical more than practical for well-structured code.
go-gremlins)Priority: P3/optional — Low incremental value over contract coverage. Consider for periodic audits rather than CI gates.
4.6 Group 6: Cognitive Complexity
What's missing: Gaze measures cyclomatic complexity (decision path count). Cognitive complexity measures how hard code is to understand — penalising nesting, breaks in linear flow, and recursion.
This is directly relevant to the article's argument about agent context cost. A function with cyclomatic complexity 10 spread across simple switch cases is easy for an agent to navigate. A function with cyclomatic complexity 10 achieved through nested conditionals and early returns is expensive.
GazeCRAP-CC = cognitive_complexity^2 x (1 - contract_coverage)^3 + cognitive_complexity--max-cognitive-complexity=15per functionPriority: P2 — Medium effort, medium-high impact.
4.7 Group 7: Architectural Drift Tracking Over Time
What's missing: All quality measurements are point-in-time. There is no trend tracking, no alerting on drift, no dashboard. Constitution Principle III — "Metrics MUST be comparable across runs. Output formats MUST be [comparable]" — directly mandates this capability; it is not optional.
dewey_store_learningwith temporal tagsdewey_semantic_search+dewey_compileto detect directional driftmx-f-architecture-trend.md/metrics trendingshows 30/60/90-day trajectory of key metrics/metricscommand sub-modemx-f-coach.mdagentDewey storage pattern:
Trend synthesis output:
Priority: P2 — Medium effort, high value for long-running projects. Depends on Group 1.
4.8 Group 8: Code Duplication Detection
What's missing: No DRY enforcement. Duplication is one of the fastest routes to architectural decay and is particularly problematic for agents, which generate duplicated patterns across files without awareness of existing implementations.
DuplicateLogicPriority: P2 — Low effort for jscpd integration; medium for Gaze-native detection.
4.9 Group 9: Supply Chain and Security
cyclonedx-gomod)unleashcommand and release workflowdivisor-guard.mdPriority: P3 — Important for production use; low implementation complexity.
4.10 Group 10: Multi-Language Coupling Architecture
Context: Every group above assumes Go as the primary target, but Unbound Force already works on Python codebases and will work on TypeScript/JavaScript. Coupling metrics that only cover Go leave architectural blind spots in polyglot projects — exactly the kind of blind spot agents exploit to accumulate entropy.
Gaze's side-effect taxonomy already solved this design problem: issue #96 (now closed) established a two-layer architecture — universal abstract types scored by Gaze core, with language-specific detail passed through opaquely. The external analyzer protocol (JSON-RPC over stdin/stdout) provides the plugin mechanism.
gaze couplingshould follow the same pattern.Architecture: Universal Metrics with Language Adapters
Layer 1 — Universal coupling model (computed by Gaze core):
These metrics are structurally identical across languages — what changes is how "module," "abstract type," and "dependency" are defined.
Layer 2 — Language adapters (one per language, reporting via the external analyzer protocol):
package foo)importstatements;golang.org/x/tools/go/packages__init__.py)ABCsubclasses +Protocolclasses / total classesimport/from ... import; static analysis viaastmodule orpydepspackage.jsonboundary)interface+abstract classdeclarations / total exported declarationsimport/exportstatements; TypeScript compiler API (ts.createProgram)package.jsonboundary)@interfaceorclasswith no concrete methodsimport/require()statements; bundler-level analysis ormadgeLanguage-Specific Considerations
Go (primary, built-in):
golang.org/x/tools/go/packagesprovides the full import graph programmatically;go/callgraphprovides function-level fan-in/fan-outPython (supported via snake-eyes):
.pyfile, a directory with__init__.py, or a namespace package (PEP 420) can all be "a module"importlib.import_module(),__import__()) and star imports (from foo import *) create invisible coupling that static analysis cannot fully resolve. This is a fundamental language limitation — the adapter should report a dynamic import warning for packages using these patternsABCsubclasses +typing.Protocolclasses are explicit abstractions; Python's duck typing is analogous to Go's implicit interfacespydepsproduces import graphs;radoncomputes function-level complexity;astmodule provides the parse tree for custom analysis. The snake-eyes analyzer can wrap thesesetattr(), runtime module attribute assignment) creates coupling invisible to import analysis. Gaze feat: add workflow phase boundaries and externalize speckit commands (closes #92 + #94) #96'sMonkeyPatchuniversal effect type detects this at the function level; the coupling adapter should flag packages containingMonkeyPatcheffects as having unmeasurable coupling riskTypeScript (future):
exportis a module;package.jsondefines package boundariests.createProgram,ts.getPreEmitDiagnostics) provides a complete dependency graph, type information, and AST — comparable to Go'sx/tools/go/packagesin capabilityinterfaceandabstract classare first-class language constructsexport { Foo } from './bar') create transitive coupling that the adapter must trace throughtsconfig.jsonpath aliases and barrel files (index.tsre-exporting everything) can obscure the true dependency graph — the adapter should resolve aliases before computing Ca/Cemadge(circular dependency detection),dependency-cruiser(configurable dependency validation with rule sets),ts-morph(programmatic TypeScript AST manipulation)JavaScript (future, shares TS adapter):
import/export) and CJS (require/module.exports) coexist — the adapter must handle both module systems and mixed-mode projects@interfaceannotations, or count classes with no concrete method bodies. Alternatively, report A as "not applicable" for JS and rely on the remaining metrics (Ca, Ce, I, D-without-A, cohesion, circular deps)require()with variable paths has the same unmeasurable-coupling problem as Python's dynamic importsmadgeanddependency-cruiserwork for JS as well as TS and could serve as the backend for a shared TS/JS adapterJSON Schema Extension
The
gaze coupling --format=jsonoutput includes alanguagefield and optionallanguage_detail:{ "packages": [ { "path": "internal/payment", "language": "go", "ca": 3, "ce": 12, "instability": 0.80, "abstractness": 0.15, "distance": 0.05, "cohesion": 0.42, "circular_deps": [], "warnings": [], "zone": "pain", "status": "violation" }, { "path": "src/auth", "language": "python", "ca": 5, "ce": 8, "instability": 0.62, "abstractness": 0.30, "distance": 0.08, "cohesion": 0.55, "circular_deps": ["src.auth -> src.models -> src.auth"], "warnings": ["dynamic_imports: 2 uses of importlib.import_module()"], "zone": "balanced", "status": "violation" }, { "path": "src/components/auth", "language": "typescript", "ca": 7, "ce": 4, "instability": 0.36, "abstractness": 0.50, "distance": 0.14, "cohesion": 0.71, "circular_deps": [], "warnings": ["barrel_reexports: index.ts re-exports 12 modules"], "zone": "balanced", "status": "ok" } ] }CI Gate Flags — Language-Aware
The same threshold flags apply across languages, but the adapter can inject language-appropriate defaults:
--max-instability=0.67— universal--max-distance=0.3— universal--no-circular-deps— universal--min-cohesion=0.5— universal--warn-dynamic-imports— Python/JS only: warn (not fail) on dynamic imports that create unmeasurable coupling--resolve-aliases— TS only: resolvetsconfig.jsonpath aliases before computing dependenciesSequencing
Priority: P1 for the universal architecture design (must be done before Group 1 ships to avoid a Go-only rewrite). P2 for the Python adapter. P3 for TS/JS.
4.11 Group 11: Branch Coverage
Context: Gaze's CRAP score uses statement coverage from
go test -coverprofile, aligned with industry standard implementations (JaCoCo, SonarQube, the original CRAP formula). This is the correct choice — Go has no native branch coverage instrumentation, and the cubic term(1 - cov/100)^3in the CRAP formula would amplify branch coverage's typically lower percentages disproportionately without providing additional diagnostic signal.What branch coverage adds: Branch coverage fills a specific gap that neither CRAP (statement coverage) nor GazeCRAP (contract coverage) addresses: conditional path coverage. If a side effect only triggers in one branch of an if-statement, Gaze detects the side effect exists but cannot tell whether tests exercise the branch that triggers it. Branch coverage directly answers "were both the true and false paths of this condition executed?"
Important: Branch coverage should be reported as a separate standalone gate, not folded into the CRAP formula. The two metrics answer different questions and should remain independent.
Go-specific note:
go test -covermode=atomicdoes NOT produce branch coverage — it produces statement coverage with atomic counter updates (for concurrent tests). Go deliberately chose block-level instrumentation over branch-level because, as Rob Pike noted, "it's hard to do branch instrumentation by rewriting the source, since branches don't appear explicitly in the source." True branch coverage in Go would require custom AST instrumentation — moderate effort but achievable./metricsoutputPriority: P2 — Medium effort for Go instrumentation; valuable as a standalone gate. Do NOT integrate into CRAP formula.
5. Prioritised Roadmap
gaze coupling)divisor-entropy.md)agent-design.md)/metricscommand +metrics-analystagent6. Sequencing Recommendation
Phase 1: Foundation (Groups 1, 2a, 3, 10-architecture)
Design the universal coupling model (Group 10) first — this ensures
gaze couplingis multi-language from day one rather than Go-only with a later rewrite. Then build the Go-native implementation (Group 1), create the entropy sentinel agent (Group 2a), and write the agent-design convention pack (Group 3). This gives the system the ability to see architectural quality for the first time.Phase 2: Accessibility (Groups 4, 2b-d)
Create the
/metricscommand andmetrics-analystagent so users and other agents can consume the metrics. Add the pre-change CRAP gate and CRAP delta to the Review Council.Phase 3: Depth (Groups 5, 6, 10-python, 11)
Add cognitive complexity and branch coverage as a standalone gate. Ship the Python coupling adapter via snake-eyes (Group 10). These strengthen the quality signal that Gaze provides across languages. Branch coverage fills the conditional-path gap that neither CRAP (statement coverage) nor GazeCRAP (contract coverage) addresses.
Phase 4: Trends and Hygiene (Groups 7, 8, 9, 10-ts/js)
Add drift tracking in Dewey, duplication detection, SBOM, and the TS/JS coupling adapter. These are valuable but depend on the foundation being in place. Mutation testing (Group 5) is available as an optional periodic audit but is not a CI gate given its overlap with contract coverage.
7. Constitution Alignment
Every addition aligns with the five constitutional principles:
.uf/artifacts/; Gaze remains usable without metrics agent/metricscommand works with or without Dewey; coupling metrics work with or without CRAP8. Discussion Questions
This RFC proposes specific additions, but several design decisions need org-wide input:
Is coupling the right P0? The report assumes package-level structural metrics are the biggest gap. Is there a more pressing blind spot we're missing?
Go-first or cross-language from the start? Gaze's side-effect taxonomy already has a universal multi-language design (issue #96, now closed). Should
gaze couplingfollow the same two-layer pattern (universal metrics + language adapters) from its first release, or ship Go-only first and add the adapter layer later? Section 4.10 proposes designing the universal model as P1 before the Go implementation ships.Build in Gaze or integrate with SonarQube? SonarQube Community Edition computes Ca/Ce equivalents and is self-hostable. Is the value of native Gaze integration (JSON schema, CI flags, same toolchain) worth the implementation effort vs. wrapping SonarQube?
What thresholds? The report proposes I≤0.67, D≤0.3, Ce≤10, cognitive complexity≤15, cohesion≥0.5. Are these appropriate starting points, or should we start more permissive and ratchet down?
Should
divisor-entropyblock or advise? Initially, should the entropy sentinel issue REQUEST_CHANGES (blocking) or COMMENT (advisory)? Blocking is stronger enforcement but risks friction during adoption.Drift tracking granularity: Per-package time-series in Dewey, or aggregate project-level snapshots? Per-package gives more diagnostic power but generates more data.
Multi-language adapter priority: Section 4.10 proposes Go → Python → TS/JS. Python has snake-eyes as a foundation; TS/JS has strong existing tooling (
dependency-cruiser,madge) but no Gaze adapter yet. Should Python or TS/JS come first after Go, or should they be parallel efforts? Are there other languages we should plan for?9. Key Insight
The article's central argument — that the audience for good code now includes agents — directly maps to a product opportunity for Unbound Force. By adding structural quality metrics (coupling, cohesion, instability) alongside the existing function-level metrics (CRAP, contract coverage), and enforcing them through the Review Council and CI gates, Unbound Force becomes a tool that doesn't just help agents write code — it helps agents write well-designed code, and actively prevents the entropy accumulation that the article warns about.
The metrics themselves are not new (Martin published them in 2002). What is new is the context: applying them as automated gates in an AI agent workflow, tracking them over time to detect drift, and using them to enforce an explicit "good design for agents" standard.
All reactions