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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ The format is based on Keep a Changelog and follows Semantic Versioning.

## [Unreleased]

## [@stackbilt/scaffold-core@1.1.0] — 2026-06-12

### Changed
- `PatternName` now includes `'workers-saas'` — multi-tenant SaaS intentions classify to this pattern (previously returned `'worker'`)
- `LocalScaffoldResult` gains two new fields: `traits: string[]` (promoted from `classification.traits`) and `tier2Recommended: boolean` (true when confidence < 0.6)
- `classify.test.ts` updated to assert `result.pattern === 'workers-saas'` for multi-tenant intentions

Closes charter#221

### Added

- **`charter score --badge`** (`score.ts`): New output mode that prints a [shields.io endpoint-schema](https://shields.io/badges/endpoint-badge) JSON payload to stdout (`{"schemaVersion":1,"label":"agent context","message":"A (92)","color":"brightgreen"}`). Grade-to-color mapping: A=brightgreen, B=green, C=yellowgreen, D=yellow, F=red. Combine with `--badge --write` to persist the payload to `.charter/badge.json` so it can be served via `raw.githubusercontent.com` as a live shields.io endpoint badge. Additive only — no existing flag, output, or exit-code behavior is changed. Exports `buildBadgePayload`, `gradeToColor`, and the `BadgePayload` type for downstream use.
Expand Down
2 changes: 1 addition & 1 deletion packages/scaffold-core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@stackbilt/scaffold-core",
"sideEffects": false,
"version": "1.0.0",
"version": "1.1.0",
"description": "Zero-dependency scaffold engine core — pattern classification, knowledge, governance, codegen, and materializer",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
3 changes: 2 additions & 1 deletion packages/scaffold-core/src/__tests__/classify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function sourcePattern(result: LocalScaffoldResult): string {
describe('scaffold domain fixtures — tenancy guardrail (#177)', () => {
it('recognizes multi-tenant SaaS with org isolation as workers-saas pattern', () => {
const result = classify('Multi-tenant SaaS API with organization-level data isolation');
// workers-saas maps to jwt-auth trait + rest route shape in the package
expect(result.pattern).toBe('workers-saas');
expect(result.traits).toContain('jwt-auth');
});

Expand All @@ -50,6 +50,7 @@ describe('scaffold domain fixtures — tenancy guardrail (#177)', () => {

it('recognizes tenant isolation with row-level security as workers-saas', () => {
const result = classify('Tenant isolation API with row-level security in D1');
expect(result.pattern).toBe('workers-saas');
expect(result.traits).toContain('jwt-auth');
});
});
Expand Down
4 changes: 2 additions & 2 deletions packages/scaffold-core/src/__tests__/package.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ describe('@stackbilt/scaffold-core package metadata', () => {
expect(pkg.name).toBe('@stackbilt/scaffold-core');
});

it('version is 1.0.0', () => {
expect(pkg.version).toBe('1.0.0');
it('version is 1.1.0', () => {
expect(pkg.version).toBe('1.1.0');
});

it('license is Apache-2.0', () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/scaffold-core/src/__tests__/types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ describe('classify and buildScaffold are implemented', () => {
expect(result.files).toBeDefined();
});

it('buildScaffold promotes traits and tier2Recommended to top-level', () => {
const result = buildScaffold('multi-tenant SaaS API with JWT auth');
expect(Array.isArray(result.traits)).toBe(true);
expect(typeof result.tier2Recommended).toBe('boolean');
expect(result.traits).toEqual(result.classification.traits);
});

it('workers-saas pattern is assignable as PatternName', () => {
const p: PatternName = 'workers-saas';
expect(p).toBe('workers-saas');
});

it('classify returns a ClassifyResult without throwing', () => {
const result = classify('build a KV-backed worker');
expect(result).toBeDefined();
Expand Down
2 changes: 1 addition & 1 deletion packages/scaffold-core/src/classify/patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export const SCORED_PATTERNS: ScoredPatternDef[] = [
},
},
{
name: 'worker' as PatternName,
name: 'workers-saas' as PatternName,
status: 'ACTIVE',
category: 'COMPUTE',
keywords: ['saas', 'tenant', 'multi-tenant', 'org', 'workspace'],
Expand Down
2 changes: 2 additions & 0 deletions packages/scaffold-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,7 @@ export function buildScaffold(
governance,
files: finalFiles,
facts,
traits: classification.traits,
tier2Recommended: classification.confidence < 0.6,
};
}
5 changes: 5 additions & 0 deletions packages/scaffold-core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

export type PatternName =
| 'worker'
| 'workers-saas'
| 'api'
| 'fullstack'
| 'scheduled'
Expand Down Expand Up @@ -132,6 +133,10 @@ export interface LocalScaffoldResult {
governance: GovernanceDocs;
files: ScaffoldFile[];
facts: ScaffoldFacts;
/** Promoted from classification.traits for convenient top-level access */
traits: string[];
/** True when classifier confidence is below 0.6 — signals LLM tier-2 may improve results */
tier2Recommended: boolean;
}

export interface ScaffoldOptions {
Expand Down