Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions .archcheck-suppressions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Suppressions for archcheck (loko constitution compliance check).
#
# Each entry is a scoped, owner-tagged, dated exemption for a pre-existing
# violation that cannot be fixed within the current feature's scope. Every
# active suppression has an `expires_on` date (≤ 90 days from creation);
# expired entries are converted back into normal failures.
#
# Schema: specs/010-constitution-compliance/contracts/suppression-file-schema.yaml
# Loader: tools/archcheck/suppression.go

# --- v1.2.0 outer-layer tightening: MCP tools still import internal/core/entities directly.
# The constitution amendment lands now (1.1.0 → 1.2.0); the actual entity-decoupling
# refactor of the 16 production tool files + their tests is queued as a follow-up
# feature (tentatively 011-mcp-entity-decoupling). 90-day hard deadline.
- rule: mcp
file: "internal/mcp/tools/*.go"
owner: "@andhi"
expires_on: "2026-08-19"
reason: |
v1.2.0 outer-layer tightening landed alongside the suppression mechanism;
the 16 MCP tool files and their tests still import internal/core/entities
for response-shape converters (systemToMap, relationshipToMap, etc.) and
use entity helpers (NormalizeName). The actual decoupling is tracked as
follow-up feature 011-mcp-entity-decoupling. Hard 90-day deadline.
notes: "Tracking: feature 011-mcp-entity-decoupling (to be created)"

- rule: mcp
file: "internal/mcp/*.go"
owner: "@andhi"
expires_on: "2026-08-19"
reason: |
Same v1.2.0 deferral — covers internal/mcp/graph_cache.go and its test
which still hold entity types. Folded into feature 011 alongside the
tools/ refactor.
notes: "Folded into feature 011-mcp-entity-decoupling"

- rule: api
file: "internal/api/handlers/handlers_test.go"
owner: "@andhi"
expires_on: "2026-08-19"
reason: |
Test file in api/handlers constructs entity fixtures directly for
response-shape assertions. Production code in api/handlers does NOT
import entities. Will be reworked when the api package gets its own
DTO layer (deferred to a future feature).
notes: "Production code in this package is already clean."

# --- Pre-existing size stragglers carried over from 009. Small overages (75>50, 208>200,
# 37>30) — each will be fixed in a focused PR. 60-day deadline so they don't drift.
- rule: cli-handler-func-size
file: cmd/watch.go
function: Execute
owner: "@andhi"
expires_on: "2026-07-20"
reason: |
Pre-existing carry-over from 009. The watch loop's Execute is 75 effective
lines (limit 50); needs extraction of the event-handling switch into a
helper. Small focused PR.
notes: "Tracking issue: TODO file before suppression expiry"

- rule: usecase-file-size
file: internal/core/usecases/build_architecture_graph.go
owner: "@andhi"
expires_on: "2026-07-20"
reason: |
Pre-existing carry-over from 009. File is 208 effective lines (limit 200).
Needs one sub-step split (likely move resolveSystemDeps into a sibling).
Small focused PR.
notes: "Tracking issue: TODO file before suppression expiry"

- rule: mcp-tool-func-size
file: internal/mcp/tools/query_dependencies.go
function: Call
owner: "@andhi"
expires_on: "2026-07-20"
reason: |
Pre-existing carry-over from 009. Handler Call is 37 effective lines
(limit 30). Needs extraction of the depth/visited-tracking loop into a
use-case helper. Will be done alongside feature 011 entity-decoupling.
notes: "Folded into feature 011-mcp-entity-decoupling"
32 changes: 14 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,21 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Run constitution audit
run: |
chmod +x scripts/audit-constitution.sh
./scripts/audit-constitution.sh
# continue-on-error so the output is always visible even when violations exist
continue-on-error: true
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'

- name: Enforce constitution (fail on violations)
run: |
echo "Constitution audit: Enforcing handler size limits..."
if ./scripts/audit-constitution.sh; then
echo "✅ All handlers comply with constitution"
exit 0
else
echo "❌ Constitution violations detected - see output above"
echo " Fix: Extract business logic to internal/core/usecases/"
echo " Limits: CLI < 150 lines, MCP tool < 80 lines (pure-data files excluded)"
exit 1
fi
- name: Run constitution audit (archcheck)
run: go run ./tools/archcheck --format=json --report=archcheck-report.json

- name: Upload archcheck report
if: always()
uses: actions/upload-artifact@v4
with:
name: archcheck-report
path: archcheck-report.json
retention-days: 14

examples:
name: Validate Examples
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,7 @@ research/
# Strategic/business docs go in research/ (gitignored)
test/
docs/superpowers/
archcheck
.archcheck-suppressions.yaml
archcheck-report.json
coverage.txt
31 changes: 19 additions & 12 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,21 @@ linters:
- revive
settings:
# Depguard is the redundant fast-path layer-import check (IDE + `task lint`).
# The full rule set lives in
# specs/009-constitution-compliance/contracts/structural-rules.yaml and is
# enforced authoritatively by `make audit-constitution` (tools/archcheck).
# Here we encode only the highest-leverage rule so that contributors get
# editor-time feedback on the most-frequently-regressed boundary:
# - cmd/ MUST NOT import internal/core/entities directly (FR-008).
# Other layer rules are enforced exclusively by archcheck — duplicating
# them in depguard would diverge over time without a meaningful benefit.
# The full rule set lives in `tools/archcheck/rules.yaml` and is enforced
# authoritatively by `task audit-constitution` (tools/archcheck). Here we
# encode the highest-leverage rules so contributors get editor-time
# feedback on the most-frequently-regressed boundaries:
# - cmd/ MUST NOT import internal/core/entities directly (Constitution v1.1.0+).
#
# TODO(011-mcp-entity-decoupling): once the suppressed files in
# .archcheck-suppressions.yaml are refactored to obtain entity types via
# use-case return values, ADD parallel rules here:
# - mcp-no-direct-entities (files: "internal/mcp/**/*.go")
# - api-no-direct-entities (files: "internal/api/**/*.go")
# That mirrors Constitution v1.2.0 in the fast-path. Today these rules are
# intentionally absent: depguard has no per-file suppression mechanism
# equivalent to archcheck's, so enabling them now would break `task lint`
# on 20 files that are knowingly suppressed in archcheck pending feature 011.
depguard:
rules:
cmd-no-direct-entities:
Expand All @@ -39,10 +46,10 @@ linters:
deny:
- pkg: "github.com/madstone-tech/loko/internal/core/entities"
desc: >-
cmd/ MUST NOT import entities directly under constitution v1.1.0;
obtain entity types via use-case return values or adapter
outputs. See .specify/memory/constitution.md and
specs/009-constitution-compliance/contracts/structural-rules.yaml.
cmd/ MUST NOT import entities directly under constitution
v1.1.0+; obtain entity types via use-case return values or
adapter outputs. See .specify/memory/constitution.md and
tools/archcheck/rules.yaml.

formatters:
enable:
Expand Down
2 changes: 1 addition & 1 deletion .specify/init-options.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"ai": "claude",
"ai_skills": true,
"branch_numbering": "sequential",
"context_file": "AGENTS.md",
"context_file": "CLAUDE.md",
"here": true,
"integration": "claude",
"script": "sh",
Expand Down
14 changes: 12 additions & 2 deletions .specify/integrations/claude.manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
{
"integration": "claude",
"version": "0.8.4",
"installed_at": "2026-05-08T16:41:04.634785+00:00",
"files": {}
"installed_at": "2026-05-20T18:29:39.722730+00:00",
"files": {
".claude/skills/speckit-analyze/SKILL.md": "2eef0fbff6cad15c9d4714d8986192387811c971a82a1135ab0404f3db0c5e90",
".claude/skills/speckit-checklist/SKILL.md": "26419fc118dcd9c4e1e977460696a04b7757b8fb0a2d1ff9c64732669deb7977",
".claude/skills/speckit-clarify/SKILL.md": "f2560f9f2007b4e995130f0c42633f08837a76a35d94e84091713a6f39bb1064",
".claude/skills/speckit-constitution/SKILL.md": "c1a044aba243ca6aff627fb5e4404feb6f1108d4f7dd174631bee3ae477d6c15",
".claude/skills/speckit-implement/SKILL.md": "da9b4d6f9894d300515c66c057cee74025b27f2238895e3c22b59c6266b5be74",
".claude/skills/speckit-plan/SKILL.md": "8141ebbce228ad0b422a84e3b995d2bd85de917b96eadd02b5fcb56fb23f2594",
".claude/skills/speckit-specify/SKILL.md": "caadc05119eca453709a0425ed88d253883f9c55da4c13a4898367653a859483",
".claude/skills/speckit-tasks/SKILL.md": "792589edf0ebf89af797c6bdda4e9d2c9938c696181d6f1484bf7a7cd090efaa",
".claude/skills/speckit-taskstoissues/SKILL.md": "99bf5ffd90dcb57b63007c7f659a5160a18ce6feb82889895808e2d277abe83b"
}
}
14 changes: 12 additions & 2 deletions .specify/integrations/pi.manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
{
"integration": "pi",
"version": "0.8.4",
"installed_at": "2026-05-08T16:40:20.174804+00:00",
"files": {}
"installed_at": "2026-05-20T18:29:13.521165+00:00",
"files": {
".pi/prompts/speckit.analyze.md": "699032fdd49afe31d23c7191f3fe7bcb1d14b081fbc94c2287e6ba3a57574fda",
".pi/prompts/speckit.checklist.md": "d7d691689fe45427c868dcf18ade4df500f0c742a6c91923fefba405d6466dde",
".pi/prompts/speckit.clarify.md": "0cc766dcc5cab233ccdf3bc4cfb5759a6d7d1e13e29f611083046f818f5812bb",
".pi/prompts/speckit.constitution.md": "58d35eb026f56bb7364d91b8b0382d5dd1249ded6c1449a2b69546693afb85f7",
".pi/prompts/speckit.implement.md": "83628415c86ba487b3a083c7a2c0f016c9073abd02c1c7f4a30cff949b6602c0",
".pi/prompts/speckit.plan.md": "5b1e9c9b5a26a1877fe3b655a9350562cf5ee88788f9030e6b8a9dc1de88b347",
".pi/prompts/speckit.specify.md": "c7b1ab7ceafc42607e86dd6c0dab0b5cb74462a93a3cdd3b5e7173468cc88bdf",
".pi/prompts/speckit.tasks.md": "a58886f29f75e1a14840007772ddd954742aafb3e03d9d1231bee033e6c1626b",
".pi/prompts/speckit.taskstoissues.md": "e84794f7a839126defb364ca815352c5c2b2d20db2d6da399fa53e4ddbb7b3ee"
}
}
14 changes: 2 additions & 12 deletions .specify/integrations/speckit.manifest.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
{
"integration": "speckit",
"version": "0.8.4",
"installed_at": "2026-05-08T16:41:04.615746+00:00",
"files": {
".specify/scripts/bash/common.sh": "dd638316259e699fd466542c77ef16af5eb198efe0447c081f86b890db414ba8",
".specify/scripts/bash/setup-plan.sh": "0d1d7a66de157b0be1385bb91aa71e5bf95550217abf47a73270dab0dc52895a",
".specify/scripts/bash/check-prerequisites.sh": "aff361639c504b95a2901493f5022788adc01a6792fd37f132de8f57782e4b80",
".specify/scripts/bash/create-new-feature.sh": "bcf4964ca0c6c78717bb42d9e66b8c7e5ee82779cd96afc5aa7b08b75abe5790",
".specify/templates/constitution-template.md": "ce7549540fa45543cca797a150201d868e64495fdff39dc38246fb17bd4024b3",
".specify/templates/checklist-template.md": "c37695297e5d3153d64f82c21223509940b13932046c7961c42d1d669516130c",
".specify/templates/tasks-template.md": "fb7a30a6e8e7319b7134bd52a26dd52fb7dd9106ab8fa08b6fb551d704dac498",
".specify/templates/spec-template.md": "785dc50d856dd92d6515eca0761e16dce0c9ba0a3cd07154fd33eae77932422a",
".specify/templates/plan-template.md": "5ad267630e370c73fe957dafa61bf76d633f3aea9d2f0b5195087d729cdd1e41"
}
"installed_at": "2026-05-20T18:29:39.733577+00:00",
"files": {}
}
6 changes: 3 additions & 3 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ Start with the simplest solution that works. Do not build for hypothetical futur
| `internal/core/entities/` | stdlib only | anything else |
| `internal/core/usecases/` | entities, stdlib | adapters, mcp, api, cmd |
| `internal/adapters/` | core (entities + usecases interfaces) | mcp, api, cmd |
| `internal/mcp/` | core, adapters | api, cmd |
| `internal/api/` | core, adapters | mcp, cmd |
| `internal/mcp/` | core/usecases, adapters | `internal/core/entities/` directly (entity types MUST be obtained via use-case return values or adapter outputs); api; cmd |
| `internal/api/` | core/usecases, adapters | `internal/core/entities/` directly (entity types MUST be obtained via use-case return values or adapter outputs); mcp; cmd |
| `cmd/` | core, adapters, mcp, api | `internal/core/entities/` directly (entity types MUST be obtained via use-case return values or adapter outputs) |

### File-Size Budgets
Expand Down Expand Up @@ -239,4 +239,4 @@ The structural-compliance check has **no per-file allowlist**. Categorical exemp
- When in doubt, refer to the ADRs in `docs/adr/` for decision context
- The machine-consumable mirror of the file-size, function-size, layer-import, and exemption rules lives at `specs/009-constitution-compliance/contracts/structural-rules.yaml`. The markdown text in this file remains canonical; the YAML is regenerated/synced by review and a CI cross-check ensures the two never diverge.

**Version**: 1.1.0 | **Ratified**: 2026-02-06 | **Last Amended**: 2026-05-08
**Version**: 1.2.0 | **Ratified**: 2026-02-06 | **Last Amended**: 2026-05-21
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,23 @@ We welcome contributions! loko is **building in public** — see our [developmen
- 💡 **Feature requests** → [Start a discussion](https://github.com/madstone-tech/loko/discussions/new?category=ideas)
- 🔧 **Pull requests** → See [CONTRIBUTING.md](CONTRIBUTING.md)

### Quality gates

loko enforces its [Clean Architecture constitution](.specify/memory/constitution.md)
mechanically. Before opening a PR, run:

```bash
task lint # gofmt, vet, golangci-lint (incl. depguard layer rules)
task test # full unit + integration suite
task audit-constitution # structural-compliance gate (file/function-size + layer-import rules)
```

`task audit-constitution` runs in well under a second and is a **required check** on `main`.
It enforces four budgets (CLI handler ≤ 50 lines, MCP handler ≤ 30, use-case file ≤ 200,
entity file ≤ 300) and the layer-import rules. New contributors: start with the one-page
[Constitution Compliance reference](docs/architecture/constitution-compliance.md) and the
feature [quickstart](specs/010-constitution-compliance/quickstart.md).

---

## 📜 License
Expand Down
5 changes: 5 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ tasks:
cmds:
- golangci-lint run

audit-constitution:
desc: Enforce loko constitution — layer-import rules + file/function-size budgets.
cmds:
- go run ./tools/archcheck

fmt:
desc: Format code
cmds:
Expand Down
18 changes: 11 additions & 7 deletions cmd/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"syscall"

"github.com/madstone-tech/loko/internal/adapters/d2"
"github.com/madstone-tech/loko/internal/adapters/encoding"
"github.com/madstone-tech/loko/internal/adapters/filesystem"
"github.com/madstone-tech/loko/internal/mcp"
"github.com/madstone-tech/loko/internal/mcp/tools"
Expand Down Expand Up @@ -75,9 +76,12 @@ func registerTools(server *mcp.Server, repo *filesystem.ProjectRepository) error
// Graph cache — shared across tools that need cache invalidation.
graphCache := server.GetGraphCache()

// Output encoder for TOON/JSON formatting.
encoder := encoding.NewEncoder()

toolList := []mcp.Tool{
tools.NewQueryProjectTool(repo),
tools.NewQueryArchitectureTool(repo),
tools.NewQueryProjectTool(repo, encoder),
tools.NewQueryArchitectureTool(repo, encoder),
tools.NewCreateSystemTool(repo),
tools.NewCreateContainerTool(repo, diagramGenerator),
tools.NewCreateComponentTool(repo),
Expand All @@ -89,14 +93,14 @@ func registerTools(server *mcp.Server, repo *filesystem.ProjectRepository) error
tools.NewBuildDocsTool(repo),
tools.NewValidateToolFull(repo, relRepo),
tools.NewValidateDiagramTool(renderer),
tools.NewQueryDependenciesToolFull(repo, relRepo, graphCache),
tools.NewQueryRelatedComponentsToolFull(repo, relRepo),
tools.NewAnalyzeCouplingToolFull(repo, relRepo),
tools.NewSearchElementsTool(repo),
tools.NewQueryDependenciesToolFull(repo, relRepo, graphCache, encoder),
tools.NewQueryRelatedComponentsToolFull(repo, relRepo, encoder),
tools.NewAnalyzeCouplingToolFull(repo, relRepo, encoder),
tools.NewSearchElementsTool(repo, encoder),
tools.NewFindRelationshipsTool(repo),
// US1: Relationship management tools
tools.NewCreateRelationshipTool(relRepo, repo, graphCache),
tools.NewListRelationshipsTool(relRepo, repo),
tools.NewListRelationshipsTool(relRepo, repo, encoder),
tools.NewDeleteRelationshipTool(relRepo, repo, graphCache),
}

Expand Down
Loading
Loading