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
10 changes: 5 additions & 5 deletions docs/ab-evaluation/case-existing-adoption.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ No spec, no architecture invariants — just code on the existing tree.
| Test files | 1 | 1 | +0 |
| Test LoC | 14 | 14 | +0 |
| Test cases | 1 | 1 | +0 |
| Total chars (artifacts + code) | 11268 | 3864 | +7404 |
| Estimated tokens | 2819 | 967 | +1852 |
| Total chars (artifacts + code) | 11212 | 3864 | +7348 |
| Estimated tokens | 2805 | 967 | +1838 |

**Detector outcomes** (META_INTEGRITY + HARDCODED_SECRET excluded — toolchain-only checks):

Expand Down Expand Up @@ -90,8 +90,8 @@ B (Vanilla) — errors: 1 warns: 3 infos: 28
| Test files | 2 | 2 | +0 |
| Test LoC | 27 | 27 | +0 |
| Test cases | 3 | 3 | +0 |
| Total chars (artifacts + code) | 12811 | 5115 | +7696 |
| Estimated tokens | 3205 | 1280 | +1925 |
| Total chars (artifacts + code) | 12732 | 5115 | +7617 |
| Estimated tokens | 3185 | 1280 | +1905 |

**Detector outcomes** (META_INTEGRITY + HARDCODED_SECRET excluded — toolchain-only checks):

Expand All @@ -111,7 +111,7 @@ B (Vanilla) — errors: 1 warns: 3 infos: 28
- **Spec ↔ code traceability**: cladding emits 1 feature(s), 2 AC(s), 1 scenario(s), 3 capability(s); vanilla has 0 of each.
- **Architecture enforcement**: cladding declares 3 layer(s) with 0 forbidden-import rule(s); vanilla has 0.
- **Detector behavior**: cladding-managed tree → 1 error(s) / 1 warn(s) / 11 info(s). Vanilla tree → 1 / 3 / 28. The detectors that gate against spec (REFERENCE_INTEGRITY, MISSING_IMPLEMENTATION, ARCHITECTURE_FROM_SPEC, CAPABILITIES_FEATURE_MAPPING) need cladding's artifacts to evaluate — without them they silently pass. The "0 errors on vanilla" therefore is **absence of signal**, not absence of drift.
- **Token cost**: cladding's cumulative artifact + code consumes ~3205 tokens vs vanilla's ~1280 (heuristic chars/4) — Δ ≈ 1925 tokens, the price of structure.
- **Token cost**: cladding's cumulative artifact + code consumes ~3185 tokens vs vanilla's ~1280 (heuristic chars/4) — Δ ≈ 1905 tokens, the price of structure.
- **Code surface**: vanilla writes 9 source file(s) / 144 LoC + 2 test file(s) / 3 test case(s); cladding writes 9 / 128 + 2 / 3. (Vanilla front-loads code, cladding front-loads spec — both converge by M2.)

## Outcome Quality (F-ba2e05)
Expand Down
32 changes: 16 additions & 16 deletions plugins/claude-code/dist/clad.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ project:

# Auto-maintained by `clad sync` (F-5b9f9f). Do not edit by hand.
inventory:
features: 255
features: 256
scenarios: 2
capabilities: 6
test_files: 237
7 changes: 4 additions & 3 deletions spec/attestation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ attested_modules:
docs/ab-evaluation/README.md: 2467808b9871dcf0
docs/ab-evaluation/case-doverunner-scale.md: 2a435b6855823d41
docs/ab-evaluation/case-efficiency-measurement.md: 348aca146a7b2faa
docs/ab-evaluation/case-existing-adoption.md: ec78b64f115d9c91
docs/ab-evaluation/case-existing-adoption.md: d9d0d649861024ce
docs/ab-evaluation/case-graph-efficiency.md: 83d3956d14a1517d
docs/ab-evaluation/case-iterative-vs-fixed-vapt.md: 1d242b8b6071f343
docs/ab-evaluation/case-payment-saas.md: ca1a9a22b933c5cf
Expand Down Expand Up @@ -113,7 +113,7 @@ attested_modules:
skills/serve/SKILL.md: f08bbdbbfeb05041
skills/status/SKILL.md: 09faadc50b3449da
skills/sync/SKILL.md: 775c0f990a52a3d9
spec.yaml: 59840b10bd055d32
spec.yaml: 82491ca5dc73636f
spec/README.md: 7c257426396d435c
spec/architecture.yaml: f0888480405a13a8
spec/features/: a4d0f0eb87fed960
Expand Down Expand Up @@ -155,7 +155,7 @@ attested_modules:
src/cli/graph.ts: bab410061b8c746a
src/cli/hook.ts: 201f82f4c89e173f
src/cli/host-onboarding.ts: b046571d4be7280c
src/cli/init.ts: ccd14da690621d88
src/cli/init.ts: 04135bc767e58e02
src/cli/intent-from-path.ts: e69862821d979f22
src/cli/measure.ts: 3a562f16589e26c2
src/cli/report.ts: 911f859b2446834b
Expand Down Expand Up @@ -586,6 +586,7 @@ attested_features:
F-6ed216f3: ok
F-6f80e7: ok
F-7076f7: ok
F-70ed1afd: ok
F-723c81dd: ok
F-7794a6bc: ok
F-77a90ac6: ok
Expand Down
34 changes: 34 additions & 0 deletions spec/features/adoption-honors-host-capabilities-70ed1afd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
id: F-70ed1afd
slug: adoption-honors-host-capabilities
title: "Existing-project adoption honors the host draft's approved capabilities"
status: done
modules:
- src/cli/init.ts
acceptance_criteria:
- id: AC-d20df076
ears: event
condition: "when an existing project is adopted through the onboarding flow and the host/LLM draft dispatch fires fully (onboarding source is 'llm')"
action: "select onboarding.capabilitiesYaml over interp.capabilitiesYaml in the scan branch"
response: "spec/capabilities.yaml carries the draft's id/title/summary/surface entries"
text: "The system shall write the draft's approved capabilities to spec/capabilities.yaml instead of the scanner's README-derived capabilities."
test_refs: ["tests/serve/init-tools.test.ts"]
- id: AC-039f62b6
ears: unwanted
condition: "if the adopted repository has no README ## headings"
response: "spec/capabilities.yaml is the draft's capabilities, never 'capabilities: []' when a fired draft provided them"
text: "The system shall not write an empty capabilities list when a fired draft supplied capabilities, and shall persist the draft's capabilities instead."
test_refs: ["tests/serve/init-tools.test.ts"]
- id: AC-6421da1f
ears: state
condition: "while the onboarding dispatch did not fire (source is 'deterministic') or no intent was provided so there is no onboarding pass"
text: "The system shall fall back to the scanner-derived capabilities, preserving prior behavior for --no-llm and no-intent scans."
test_refs: ["tests/cli/init.test.ts"]
- id: AC-aa2d0e2c
ears: ubiquitous
text: "The system shall keep docs/conventions.md and spec/architecture.yaml sourced from the scanner (code-grounded), unaffected by the capability-source preference."
test_refs: ["tests/scenarios/existing-adoption-lifecycle.test.ts"]
design_impact:
classification: none
rationale: "Behavioral fix to how the init scan-branch sources spec/capabilities.yaml; it does not alter cladding's own capabilities, architecture, or project-context artifacts."
status: resolved
artifacts: []
1 change: 1 addition & 0 deletions spec/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ features:
F-6ed216f3: {slug: interactive-drift-profile, status: done, modules: 5}
F-6f80e7: {slug: claude-code-dogfood, status: done, modules: 1}
F-7076f7: {slug: smoke-probe-demand, status: done, modules: 3}
F-70ed1afd: {slug: adoption-honors-host-capabilities, status: done, modules: 1}
F-723c81dd: {slug: agent-interpreter-rule, status: done, modules: 7}
F-7794a6bc: {slug: impact-blast-radius, status: done, modules: 3}
F-77a90ac6: {slug: scan-layer-glob-source-root, status: done, modules: 1}
Expand Down
15 changes: 14 additions & 1 deletion src/cli/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,20 @@ export async function runInit(opts: InitOptions = {}): Promise<InitResult> {
// directly instead of re-parsing docs/project-context.md. LLM
// refinement (when a dispatcher is available) adds `summary` +
// `surface` per entry; deterministic mode ships id + title only.
writeArtifact(cwd, 'spec/capabilities.yaml', interp.capabilitiesYaml, created, proposals);
//
// v0.9.1 — an existing-project adoption still carries the host/LLM's
// approved, domain-aware capabilities via the onboarding pass. The scan
// interpreter can't see them: its total-fallback fires on the absent
// CONVENTIONS_MD sentinel (a host draft never emits it) and re-derives
// capabilities from README headings — or `[]` when there are none —
// silently discarding the draft's schema-validated 3–8 entries. Prefer
// the onboarding capabilities when the dispatcher genuinely fired
// (`source === 'llm'`), mirroring the greenfield branch below; the
// scanner still owns conventions.md + architecture.yaml, which are
// code-grounded and correct for a real codebase.
const capabilitiesYaml =
onboarding?.source === 'llm' ? onboarding.capabilitiesYaml : interp.capabilitiesYaml;
writeArtifact(cwd, 'spec/capabilities.yaml', capabilitiesYaml, created, proposals);

// v0.3.30 — scenarios are not auto-extracted from observed code.
// A user journey is *intent*, not architecture, so cladding leaves
Expand Down
11 changes: 11 additions & 0 deletions tests/serve/init-tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {dirname, join} from 'node:path';
import {Client} from '@modelcontextprotocol/sdk/client/index.js';
import {InMemoryTransport} from '@modelcontextprotocol/sdk/inMemory.js';
import {afterEach, beforeEach, describe, expect, test} from 'vitest';
import {parse as parseYaml} from 'yaml';
import {refineOnboarding, resolveOnboardingReview} from '../../src/cli/clarify.js';
import {prepareHostClarify, prepareHostInit, renderHostDraft} from '../../src/cli/host-onboarding.js';
import {runInit} from '../../src/cli/init.js';
Expand Down Expand Up @@ -427,6 +428,16 @@ describe('serve/server — natural-language init tools', () => {
const conventions = readFileSync(join(dir, 'docs', 'conventions.md'), 'utf8');
expect(conventions).toContain('derived from observed code');
expect(conventions).toContain('## Observed style');
// Regression: an existing-project adoption with no README `##` headings must still
// persist the host draft's approved capabilities — not land `capabilities: []`.
const capabilitiesBody = readFileSync(join(dir, 'spec', 'capabilities.yaml'), 'utf8');
expect(capabilitiesBody).not.toContain('capabilities: []');
const capabilities = (parseYaml(capabilitiesBody) as {capabilities?: Array<{id?: string}>}).capabilities ?? [];
expect(capabilities.length).toBeGreaterThanOrEqual(3);
const capabilityIds = capabilities.map((capability) => capability.id);
for (const id of ['payments', 'audit', 'webhooks']) {
expect(capabilityIds).toContain(id);
}
} finally {
await cleanup();
}
Expand Down
Loading