tooling/skill-app/skill/SKILL.md
use when setting up APP projects, adding host apps, creating or updating Cases, introducing packages, classifying shared code across cases/packages/core/shared, maintaining case .us.md artifacts, validating APP grammar, reviewing structural drift, and adapting existing projects incrementally with the canonical /app workflow
npx skillsauth add jhenriquedev/app-protocol appInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This is the active PRD revision of the canonical /app skill.
It keeps the operational content of the previous revision while reducing repetition
and improving scanability.
1.1.6-prd[email protected]prdbackend, portal, agent, worker, or lambdasusuario_criardomain, api, ui, web, mobile, stream, and agentic surfacespackages/ and expose them correctly through host registriescases/, packages/, core/shared/, or requires protocol evolution<case>.us.md when semantics, contracts, or composition changeIf you do not know APP yet, this skill should guide you through the canonical workflow instead of assuming prior protocol knowledge.
APP is the protocol layer of the AI-First Programming Paradigm.
/app is the canonical skill for applying that protocol in real projects.
Use /app to inspect this repository.Set up a new APP project using /app.Add an agent host app using /app.Create case usuario_criar using /app.Introduce packages/ for shared HTTP clients using /app.Adapt this existing project to APP incrementally using /app.Implement the api surface for usuario_criar using /app.Validate this repository with APP grammar.Review drift in this project using /app.Create usuario_criar with domain, api, and usuario_criar.us.md.spec.md as its normative protocol reference/app ProfileAPP defines baseline protocol grammar.
/app defines a stricter operational profile for agents working on APP projects.
| Topic | APP baseline | /app profile |
| -------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| test() | strongly recommended | required on every surface created or edited by the agent |
| <case>.us.md | optional support artifact | required for new Cases, new surfaces, and semantic changes |
| workflow | free | inspect → specify → create/implement → validate → review |
| validation | may be partial | must happen before task closure |
| agentic completeness | agentic optional; app-level agentic host formalized in current spec | when the task requires agentic at Case or app level, the full formal definition is mandatory; partial or placeholder agentic layers are non-conformant in /app |
| subagents | outside protocol scope | required when supported and parallel work is useful |
inspect/app turn, read this SKILL.md and the adjacent installed spec.md before acting; if spec.md is missing, report incomplete skill installation or drifthandler() thin and free of business logic<case>.us.md when semantics changetest() on every touched surfacepackages/ → core/ → cases/ → apps/
| Layer | Role |
| ------------- | ----------------------------------------------------------------------- |
| packages/ | shared project code exposed by the host through ctx.packages |
| core/ | protocol contracts, base classes, types, integration interfaces |
| cases/ | capabilities; shareable business logic lives here |
| apps/ | hosts; select Cases, providers, packages, runtime, and deployment model |
| Relationship | Rule |
| ----------------------------------------- | ------------------------------- |
| cases/ → core/ | allowed |
| apps/ → cases/ | allowed through registry |
| apps/ → packages/ | allowed |
| cases/ → packages/ direct import | forbidden; use ctx.packages |
| Case A → Case B direct import | forbidden; use ctx.cases |
| cases/ → apps/ | forbidden |
/app| Task | Canonical result |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| new APP project | create canonical layers, first host app, first registry, and first Case path |
| new host app | add apps/<app>/app.ts and apps/<app>/registry.ts with only needed _cases, _providers, _packages |
| new package | add shared project code under packages/ and expose it per app through _packages / ctx.packages |
| new core/shared/ artifact | add only if it is a protocol-level contract or shared structural shape |
| new canonical surface | stop normal implementation flow and treat as protocol evolution |
| existing-project adoption | carve out APP-managed areas incrementally; do not force a full rewrite unless requested |
A Case is one cohesive capability organized inside its own folder.
<entity>_<verb>cases/<domain>/<case>/<case>.<surface>.case.<ext><case>.us.md<case>.us.mdThe canonical support artifact is <case>.us.md.
It records operational intent without replacing domain.case.ts.
Required in /app when:
Optional only when:
Implement only the surfaces the task needs.
| Surface | File | Goal | Required in /app | Optional | Key Rules |
| ----------- | -------------------------- | --------------------------------------------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| domain | <case>.domain.case.ts | pure semantics, invariants, validation, schemas, examples | caseName(), description(), inputSchema(), outputSchema(), test() | validate, invariants, valueObjects, enums, examples, definition | no I/O; consumed manually by other surfaces; no auto-wiring of domain.validate() |
| api | <case>.api.case.ts | backend execution, auth, orchestration, response | handler(input), test(), one execution center: _service or _composition | router, _validate, _authorize, _repository | handler() receives business input; router() only binds transport; _composition uses ctx.cases |
| ui | <case>.ui.case.ts | self-contained visual unit | view(), test() | _viewmodel, _service, _repository, setState | general visual surface; must not do direct cross-case composition |
| web | <case>.web.case.ts | self-contained visual unit for web runtimes | view(), test() | _viewmodel, _service, _repository, setState | specialized visual surface; shares APP grammar but not a required concrete contract with ui or mobile |
| mobile | <case>.mobile.case.ts | self-contained visual unit for mobile runtimes | view(), test() | _viewmodel, _service, _repository, setState | specialized visual surface; shares APP grammar but not a required concrete contract with ui or web |
| stream | <case>.stream.case.ts | event consumption, publication, declarative recovery | handler(event), test(), one execution center: _service or _composition | subscribe, recoveryPolicy, _consume, _repository, _publish | subscribe() and recoveryPolicy() are declarative; recoveryPolicy() must be deterministic and free of I/O |
| agentic | <case>.agentic.case.ts | discovery, tool contract, policy, MCP integration | discovery(), context(), prompt(), tool(), test() when surface exists | mcp, rag, policy, examples | tool.execute() delegates to a canonical surface; no shadow business logic; if the task requires the agentic layer, /app requires the full formal definition, not a partial stub |
domain is consumed manually by other surfacesdomain.validate() into api, ui, or streamdomain: I/O, HTTP, persistence, logging, rendering, arbitrary side effectshandler() receives business input, not raw HTTP requests_service and _composition are mutually exclusive as the main execution center_composition exists, cross-case orchestration happens through ctx.casesGrammar:
view ↔ _viewmodel ↔ _service ↔ _repository
ui is the general visual surface in APPui must not do direct cross-case compositionGrammar:
view ↔ _viewmodel ↔ _service ↔ _repository
web is the visual surface specialized for web runtimesweb shares the APP visual grammar, but it is not required to reuse the same concrete contract as uiweb must not do direct cross-case compositionGrammar:
view ↔ _viewmodel ↔ _service ↔ _repository
mobile is the visual surface specialized for mobile runtimesmobile shares the APP visual grammar, but it is not required to reuse the same concrete contract as ui or webmobile must not do direct cross-case compositionsubscribe() is declarative bindingrecoveryPolicy() is declarative, deterministic, serializable, and free of I/O_publish() does not replace subscribe() or the runtimemcp() controls Case-level exposure and presentation; it does not redefine executiontool.execute() must delegate to a canonical surfaceagentic is created or revised, /app requires the full formal contract to be materially defined: discovery(), context(), prompt(), tool(), and test()policy(), mcp(), rag(), and examples() whenever the capability semantics or host runtime require them; do not leave those concerns implicitapps/agent/; agentic.case.ts alone is not sufficientapps/agent/; Case-level mcp() metadata alone is not enoughTo remove ambiguity, /app treats the templates below as normative for
class-based APP projects.
Rules for all templates:
domain — exact templateThis is the canonical shape of <case>.domain.case.ts.
import {
AppSchema,
BaseDomainCase,
Dict,
DomainExample,
} from "../../../core/domain.case";
export interface MyCaseInput {
value: string;
}
export interface MyCaseOutput {
ok: boolean;
}
export class MyCaseDomain extends BaseDomainCase<MyCaseInput, MyCaseOutput> {
public caseName(): string {
return "my_case";
}
public description(): string {
return "Describe the capability in domain terms.";
}
public inputSchema(): AppSchema {
return {
type: "object",
properties: {
value: { type: "string" },
},
required: ["value"],
};
}
public outputSchema(): AppSchema {
return {
type: "object",
properties: {
ok: { type: "boolean" },
},
required: ["ok"],
};
}
public validate(input: MyCaseInput): void {
if (!input.value) {
throw new Error("value is required");
}
}
public invariants(): string[] {
return ["value must be present"];
}
public valueObjects(): Dict<unknown> {
return {};
}
public enums(): Dict<unknown> {
return {};
}
public examples(): DomainExample<MyCaseInput, MyCaseOutput>[] {
return [
{
name: "basic",
description: "Minimal valid example",
input: { value: "x" },
output: { ok: true },
},
];
}
public async test(): Promise<void> {
const def = this.definition();
if (!def.caseName) throw new Error("caseName is empty");
this.validate({ value: "x" });
}
}
Non-negotiable interpretation:
caseName(), description(), inputSchema(), and outputSchema() are the required public contractdomain does not receive ctx and should not have transport or infrastructure concernsvalidate() is pure domain validation; there is no automatic wiring into other surfacestest() is required in /app whenever the surface is created or editedapi — exact template for atomic CaseThis is the canonical shape of <case>.api.case.ts when the Case is atomic.
import {
ApiContext,
ApiResponse,
BaseApiCase,
} from "../../../core/api.case";
import { AppCaseError } from "../../../core/shared/app_structural_contracts";
import { MyCaseInput, MyCaseOutput } from "./my_case.domain.case";
export class MyCaseApi extends BaseApiCase<MyCaseInput, MyCaseOutput> {
constructor(ctx: ApiContext) {
super(ctx);
}
public async handler(
input: MyCaseInput
): Promise<ApiResponse<MyCaseOutput>> {
return this.execute(input);
}
public router(): unknown {
return {
method: "POST",
path: "/my-case",
handler: (req: { body: MyCaseInput }) => this.handler(req.body),
};
}
public async test(): Promise<void> {
const result = await this.handler({ value: "x" });
if (!result.success) throw new Error("handler returned failure");
}
protected async _validate(input: MyCaseInput): Promise<void> {
if (!input.value) {
throw new AppCaseError("VALIDATION_FAILED", "value is required");
}
}
protected async _authorize(_input: MyCaseInput): Promise<void> {}
protected _repository(): unknown {
return this.ctx.db;
}
protected async _service(input: MyCaseInput): Promise<MyCaseOutput> {
return {
ok: input.value.length > 0,
};
}
}
api — exact template for composed CaseThis is the canonical shape of <case>.api.case.ts when the Case orchestrates
other Cases.
import {
ApiContext,
ApiResponse,
BaseApiCase,
} from "../../../core/api.case";
import { AppCaseError } from "../../../core/shared/app_structural_contracts";
import { MyCaseInput, MyCaseOutput } from "./my_case.domain.case";
type ExpectedCasesMap = {
other_domain?: {
other_case?: {
api?: {
handler(input: { value: string }): Promise<ApiResponse<{ ok: boolean }>>;
};
};
};
};
export class MyCaseApi extends BaseApiCase<MyCaseInput, MyCaseOutput> {
constructor(ctx: ApiContext) {
super(ctx);
}
public async handler(
input: MyCaseInput
): Promise<ApiResponse<MyCaseOutput>> {
return this.execute(input);
}
public router(): unknown {
return {
method: "POST",
path: "/my-case",
handler: (req: { body: MyCaseInput }) => this.handler(req.body),
};
}
public async test(): Promise<void> {
const result = await this.handler({ value: "x" });
if (!result.success) throw new Error("handler returned failure");
}
protected async _validate(input: MyCaseInput): Promise<void> {
if (!input.value) {
throw new AppCaseError("VALIDATION_FAILED", "value is required");
}
}
protected async _authorize(_input: MyCaseInput): Promise<void> {}
protected _repository(): unknown {
return this.ctx.db;
}
protected async _composition(
input: MyCaseInput
): Promise<MyCaseOutput> {
const cases = this.ctx.cases as ExpectedCasesMap | undefined;
const dependency = await cases?.other_domain?.other_case?.api?.handler({
value: input.value,
});
if (!dependency?.success || !dependency.data?.ok) {
throw new AppCaseError("COMPOSITION_FAILED", "dependency failed");
}
return {
ok: true,
};
}
}
Non-negotiable interpretation:
handler(input) is mandatory and must delegate to this.execute(input)handler() receives business input, never raw transport inputrouter() is optional and only binds transport to handler()_service for atomic or _composition for composed_composition exists, cross-case orchestration must use ctx.cases, never direct Case imports_repository() is for persistence or integrations local to the Case, not cross-case compositionui — exact templateThis is the canonical shape of <case>.ui.case.ts.
import {
BaseUiCase,
UIState,
UiContext,
} from "../../../core/ui.case";
interface MyCaseState extends UIState {
value: string;
loading: boolean;
error: string | null;
}
export class MyCaseUi extends BaseUiCase<MyCaseState> {
constructor(ctx: UiContext) {
super(ctx, {
value: "",
loading: false,
error: null,
});
}
public view(): unknown {
const vm = this._viewmodel();
return {
type: "form",
fields: vm.fields,
submitDisabled: vm.submitDisabled,
feedback: vm.feedback,
onSubmit: () => this._service(),
};
}
public async test(): Promise<void> {
this.setState({ value: "x" });
await this._service();
}
protected _viewmodel() {
const { value, loading, error } = this.state;
return {
fields: [{ name: "value", value, label: "Value", type: "text" }],
submitDisabled: loading || !value,
feedback: error ? { type: "error", message: error } : null,
};
}
protected async _service(): Promise<void> {
this.setState({ loading: true, error: null });
try {
await this._repository({ value: this.state.value });
this.setState({ loading: false });
} catch (err) {
this.setState({ loading: false, error: (err as Error).message });
}
}
protected async _repository(input: { value: string }): Promise<unknown> {
return this.ctx.api?.request({
method: "POST",
url: "/my-case",
body: input,
});
}
}
Non-negotiable interpretation:
view() is the required public entrypointview ↔ _viewmodel ↔ _service ↔ _repositorysuper(ctx, initialState)setState() for local state transitionsui does not define _composition and must not perform direct cross-case orchestrationweb — exact templateThis is the canonical shape of <case>.web.case.ts.
import {
BaseWebCase,
WebContext,
WebState,
} from "../../../core/web.case";
interface MyCaseState extends WebState {
value: string;
loading: boolean;
error: string | null;
}
export class MyCaseWeb extends BaseWebCase<MyCaseState> {
constructor(ctx: WebContext) {
super(ctx, {
value: "",
loading: false,
error: null,
});
}
public view(): unknown {
const vm = this._viewmodel();
return {
type: "form",
fields: vm.fields,
submitDisabled: vm.submitDisabled,
feedback: vm.feedback,
onSubmit: () => this._service(),
};
}
public async test(): Promise<void> {
this.setState({ value: "x" });
await this._service();
}
protected _viewmodel() {
const { value, loading, error } = this.state;
return {
fields: [{ name: "value", value, label: "Value", type: "text" }],
submitDisabled: loading || !value,
feedback: error ? { type: "error", message: error } : null,
};
}
protected async _service(): Promise<void> {
this.setState({ loading: true, error: null });
try {
await this._repository({ value: this.state.value });
this.setState({ loading: false });
} catch (err) {
this.setState({ loading: false, error: (err as Error).message });
}
}
protected async _repository(input: { value: string }): Promise<unknown> {
return this.ctx.api?.request({
method: "POST",
url: "/my-case",
body: input,
});
}
}
Non-negotiable interpretation:
view() is the required public entrypointview ↔ _viewmodel ↔ _service ↔ _repositoryweb may define browser/web-specific context details without reusing the ui contractweb does not define _composition and must not perform direct cross-case orchestrationmobile — exact templateThis is the canonical shape of <case>.mobile.case.ts.
import {
BaseMobileCase,
MobileContext,
MobileState,
} from "../../../core/mobile.case";
interface MyCaseState extends MobileState {
value: string;
loading: boolean;
error: string | null;
}
export class MyCaseMobile extends BaseMobileCase<MyCaseState> {
constructor(ctx: MobileContext) {
super(ctx, {
value: "",
loading: false,
error: null,
});
}
public view(): unknown {
const vm = this._viewmodel();
return {
type: "form",
fields: vm.fields,
submitDisabled: vm.submitDisabled,
feedback: vm.feedback,
onSubmit: () => this._service(),
};
}
public async test(): Promise<void> {
this.setState({ value: "x" });
await this._service();
}
protected _viewmodel() {
const { value, loading, error } = this.state;
return {
fields: [{ name: "value", value, label: "Value", type: "text" }],
submitDisabled: loading || !value,
feedback: error ? { type: "error", message: error } : null,
};
}
protected async _service(): Promise<void> {
this.setState({ loading: true, error: null });
try {
await this._repository({ value: this.state.value });
this.setState({ loading: false });
} catch (err) {
this.setState({ loading: false, error: (err as Error).message });
}
}
protected async _repository(input: { value: string }): Promise<unknown> {
return this.ctx.api?.request({
method: "POST",
url: "/my-case",
body: input,
});
}
}
Non-negotiable interpretation:
view() is the required public entrypointview ↔ _viewmodel ↔ _service ↔ _repositorymobile may define mobile-specific context details without reusing the web or ui contractmobile does not define _composition and must not perform direct cross-case orchestrationstream — exact template for atomic CaseThis is the canonical shape of <case>.stream.case.ts when the Case is atomic.
import {
AppStreamRecoveryPolicy,
BaseStreamCase,
StreamContext,
StreamEvent,
} from "../../../core/stream.case";
export interface MyStreamPayload {
value: string;
}
export interface MyStreamOutput {
ok: boolean;
}
export class MyCaseStream extends BaseStreamCase<
MyStreamPayload,
MyStreamOutput
> {
constructor(ctx: StreamContext) {
super(ctx);
}
public async handler(event: StreamEvent<MyStreamPayload>): Promise<void> {
await this.pipeline(event);
}
public subscribe(): unknown {
return {
topic: "my_case_requested",
handler: (event: StreamEvent<MyStreamPayload>) => this.handler(event),
};
}
public recoveryPolicy(): AppStreamRecoveryPolicy {
return {
retry: {
maxAttempts: 3,
},
};
}
public async test(): Promise<void> {
await this.handler({
type: "my_case_requested",
payload: { value: "x" },
});
}
protected _repository(): unknown {
return this.ctx.db;
}
protected async _consume(
event: StreamEvent<MyStreamPayload>
): Promise<MyStreamPayload> {
return event.payload;
}
protected async _service(
input: MyStreamPayload
): Promise<MyStreamOutput> {
return {
ok: input.value.length > 0,
};
}
protected async _publish(output: MyStreamOutput): Promise<void> {
if (!output.ok) return;
await this.ctx.eventBus?.publish("my_case_processed", output);
}
}
stream — exact template for composed CaseThis is the canonical shape of <case>.stream.case.ts when the stream Case
orchestrates other Cases.
import {
AppStreamRecoveryPolicy,
BaseStreamCase,
StreamContext,
StreamEvent,
} from "../../../core/stream.case";
type ExpectedCasesMap = {
other_domain?: {
other_case?: {
api?: {
handler(input: { value: string }): Promise<{ success: boolean }>;
};
};
};
};
export interface MyStreamPayload {
value: string;
}
export class MyCaseStream extends BaseStreamCase<MyStreamPayload, void> {
constructor(ctx: StreamContext) {
super(ctx);
}
public async handler(event: StreamEvent<MyStreamPayload>): Promise<void> {
await this.pipeline(event);
}
public subscribe(): unknown {
return {
topic: "my_case_requested",
handler: (event: StreamEvent<MyStreamPayload>) => this.handler(event),
};
}
public recoveryPolicy(): AppStreamRecoveryPolicy {
return {
retry: {
maxAttempts: 3,
},
};
}
public async test(): Promise<void> {
await this.handler({
type: "my_case_requested",
payload: { value: "x" },
});
}
protected _repository(): unknown {
return this.ctx.db;
}
protected async _composition(
event: StreamEvent<MyStreamPayload>
): Promise<void> {
const cases = this.ctx.cases as ExpectedCasesMap | undefined;
const result = await cases?.other_domain?.other_case?.api?.handler({
value: event.payload.value,
});
if (!result?.success) {
throw new Error("dependency failed");
}
}
}
Non-negotiable interpretation:
handler(event) is mandatory and must delegate to this.pipeline(event)subscribe() is optional and declarative; it binds transport onlyrecoveryPolicy() is optional and must return pure metadata_service for atomic or _composition for composed_consume → _service → _publish_composition(event) and resolves other Cases via ctx.casesagentic — exact templateThis is the canonical shape of <case>.agentic.case.ts.
import {
AgenticContext,
AgenticDiscovery,
AgenticExecutionContext,
AgenticExample,
AgenticMcpContract,
AgenticPolicy,
AgenticPrompt,
AgenticRagContract,
AgenticToolContract,
BaseAgenticCase,
} from "../../../core/agentic.case";
import { ApiResponse } from "../../../core/api.case";
import { toAppCaseError } from "../../../core/shared/app_structural_contracts";
import {
MyCaseDomain,
MyCaseInput,
MyCaseOutput,
} from "./my_case.domain.case";
type ExpectedCasesMap = {
my_domain?: {
my_case?: {
api?: {
handler(input: MyCaseInput): Promise<ApiResponse<MyCaseOutput>>;
};
};
};
};
export class MyCaseAgentic extends BaseAgenticCase<
MyCaseInput,
MyCaseOutput
> {
constructor(ctx: AgenticContext) {
super(ctx);
}
protected domain(): MyCaseDomain {
return new MyCaseDomain();
}
public discovery(): AgenticDiscovery {
return {
name: this.domainCaseName() ?? "my_case",
description:
this.domainDescription() ?? "Describe the capability for agent discovery.",
category: "my_domain",
tags: ["my_domain", "my_case"],
};
}
public context(): AgenticExecutionContext {
return {
requiresAuth: false,
requiresTenant: false,
dependencies: ["my_case.domain", "my_case.api"],
constraints: ["Execution must follow the canonical surface."],
};
}
public prompt(): AgenticPrompt {
return {
purpose: "Explain when and why an agent should use this capability.",
whenToUse: ["When this capability is the canonical path."],
whenNotToUse: ["When another Case is the canonical path."],
constraints: ["Do not bypass the canonical execution surface."],
};
}
public tool(): AgenticToolContract<MyCaseInput, MyCaseOutput> {
const inputSchema = this.domainInputSchema();
const outputSchema = this.domainOutputSchema();
if (!inputSchema || !outputSchema) {
throw new Error("agentic surface requires domain schemas");
}
return {
name: "my_case",
description: "Execute the canonical my_case capability.",
inputSchema,
outputSchema,
isMutating: false,
requiresConfirmation: false,
execute: async (input, ctx) => {
const cases = ctx.cases as ExpectedCasesMap | undefined;
const result = await cases?.my_domain?.my_case?.api?.handler(input);
if (!result?.success || !result.data) {
throw toAppCaseError(
result?.error,
"my_case API surface did not return data"
);
}
return result.data;
},
};
}
public mcp(): AgenticMcpContract | undefined {
return undefined;
}
public rag(): AgenticRagContract | undefined {
return undefined;
}
public policy(): AgenticPolicy | undefined {
return undefined;
}
public examples(): AgenticExample<MyCaseInput, MyCaseOutput>[] {
return super.examples();
}
public async test(): Promise<void> {
this.validateDefinition();
this.definition();
}
}
Non-negotiable interpretation:
discovery(), context(), prompt(), tool(), and test() are required in /apptool().execute() must delegate to a canonical surface; it must not reimplement business logicdomain() hook is the canonical way to derive description and schemas from the domain surfacemcp(), rag(), policy(), and examples() must be made explicit whenever they matter semantically or operationally/app| Type | Center | Meaning |
| -------- | ------------------ | ---------------------------------------------- |
| atomic | _service() | local capability logic, no orchestration |
| composed | _composition() | orchestrates other Cases through ctx.cases |
handler() never carries business logic; it delegates to the canonical pipelinectx.cases and ctx.packages_cases exposes constructors, not shared runtime instancesUse these playbooks for repository-structure tasks that are broader than a single Case.
packages/, core/, cases/, apps/packages/ may start empty; do not invent packages before there is shared project code to exposeapps/<app>/app.ts and apps/<app>/registry.tsdomain first and add only the needed surfacesapps/<app>/registry.ts selecting only the Cases, providers, and packages that host actually needsapps/<app>/app.ts as the host bootstrap for that runtimeagent as the canonical generic host name for agentic runtimes; use chatbot only when the host is explicitly conversationalbackend, portal, agent, worker, and lambdas share the same boot code; they share responsibilities, not identical implementationIf the task creates or revises apps/agent/, /app MUST treat the agentic
layer as a complete host concern, not as an optional enhancement.
Required registry definition:
AgenticRegistry on top of AppRegistrylistAgenticCases()getAgenticSurface(ref)instantiateAgentic(ref, ctx)buildCatalog(ctx)resolveTool(toolName, ctx)listMcpEnabledTools(ctx)core/shared/ through an abstract adapter contract such as BaseAppMcpAdapter_providers rather than in core/shared/_providers.mcpAdapters.stdio and _providers.mcpAdapters.httpRequired apps/agent/app.ts responsibilities:
bootstrap(config)createAgenticContext(parent?)buildAgentCatalog(parent?)buildSystemPrompt(parent?)resolveTool(toolName, parent?)executeTool(toolName, input, parent?)initializeMcp(params?, parent?)listMcpTools(parent?)listMcpResources(parent?)readMcpResource(uri, parent?)callMcpTool(name, input, parent?)publishMcp()validateAgenticRuntime()Required runtime semantics:
agentic surfacesAgenticDefinition automatically from AgenticRegistry; do not hand-curate per-tool semantic fields in the hostAgenticContext is materialized per executionrequireConfirmation and executionMode are enforced by the host/runtimeprompt, discovery, context, and runtime policy/catalog must expose the full projected semantic payload from the registryagentic semanticsapps/agent/ conformance exposes an HTTP boundary plus at least one real MCP boundaryinitialize, tools/list, resources/list, resources/read, tools/call_mcp() as a canonical host method; use explicit host methods such as publishMcp(), listMcpTools(), and callMcpTool()stdio, do not present it as just another long-running HTTP service; document and wire it as a separate process because the MCP client must own stdin/stdout directlyinitializeMcp() must not assume exact protocol-version equality if the host claims support for more than one compatible revision; declare supported versions explicitly and reject only truly unsupported versionspackages/ Entrypackages/ for shared project code selected by hosts, not for protocol contractsregistry._packagesctx.packages in contextual surfacescore/shared/ Artifactcore/shared/ only if it is a protocol-level context, host contract, infrastructure contract, or structural shape with cross-project meaningpackages/spec.md change, and release acceptancepackages/, core/shared/, or local Case/app code instead of inventing a new canonical surface<case>.us.md Contract<case>.us.md is the agent support artifact for specifying, reviewing, and
validating a capability. It documents operational intent and does not replace
domain.case.ts.
# US: <case_name>
## Capability
## Context
## Input Requirements
## Output Requirements
## Validation Rules
## Business Invariants
## Surfaces Involved
## Composition
## Events / recovery / policy
## Validation Scenarios
## Open Questions
## Status
Ask for human approval before implementation when:
If the task is already clear and localized, the agent may create/update
<case>.us.md, implement immediately after, and present both at closure.
inspect → specify → create/implement → validate → review
| Step | Goal | Minimum Output / Rule |
| ------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| inspect | understand current topology before acting | relevant Cases, surfaces, apps, registries, conformance or drift signals; does not modify code |
| specify | materialize or update <case>.us.md | every relevant semantic change must be reflected there |
| create | scaffold a new Case or surface | create domain first for new Cases; scaffold only needed surfaces; include test() from the start; create <case>.us.md together |
| implement | write or adjust a surface inside grammar | respect project conventions; do not invent slots; keep semantics local; update test() |
| validate | check structural, behavioral, operational conformance | use tooling when available; otherwise use manual grammar checklist, review test(), run project validations, and cross-check against <case>.us.md |
| review | inspect final result before closure | focus on grammar violations, drift, surface inconsistency, composition/recovery/agentic risk |
| If the task is... | Then the skill should... |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| new project bootstrap | inspect repo state, scaffold canonical layers, add first host app, and validate minimal APP topology |
| new host app | inspect runtime needs, create app.ts + registry.ts, wire only needed Cases/providers/packages, then validate host semantics; if the host is agent, require the complete formal agentic host definition |
| package introduction | classify the shared code as packages/, expose it through _packages, and validate ctx.packages usage |
| core/shared/ addition | check whether it is truly protocol-level; if not, keep it out of core/shared/ |
| new canonical surface proposal | stop normal implementation and switch to protocol-evolution guidance |
| existing-project adaptation | use incremental adoption, preserve bounded legacy areas, and avoid broad rewrites unless requested |
<case>.<surface>.case.<ext>cases/<domain>/<case>/apps/<app>/app.ts and apps/<app>/registry.tsregistry.ts uses _cases, _providers, and _packages according to protocol semanticspackages/ additions are exposed through _packages, not direct Case importscore/shared/ additions remain protocol-level instead of project-utility drift_service and _composition do not compete for the same execution centerhandler() delegatesdomain remains puretest()<case>.us.md exists when required by /appdomain reflects the correct capabilityapi, ui, web, mobile, stream, and agentic do not contradict the domainctx.casesapps/agent/ semantics are explicit and complete rather than implied by Case-level metadatactx.cases and ctx.packages remains correctapps/agent/ enforces requireConfirmation and executionMode at runtimeapps/agent/ resolves and publishes tools through AgenticRegistryapps/agent/ keeps HTTP and MCP publication aligned to the same catalog and policy semantics_providers, while the abstract MCP contract remains in core/shared/apps/agent/ does not conflate stdio MCP startup with aggregate HTTP dev flowsapps/agent/ does not confuse ordinary HTTP routes with a remote MCP boundaryusuario_criarExample prompt:
Create case usuario_criar using /app.
Expected agent behavior:
usuario_criar.us.mdusuario_criar.domain.case.tsusuario_criar.api.case.tstest() on each touched surface<case>.us.mdExpected invariants:
Expected structure:
cases/identidade/usuario_criar/
usuario_criar.us.md
usuario_criar.domain.case.ts
usuario_criar.api.case.ts
test() ModelIn baseline APP, test() is strongly recommended.
In /app, test() is required for every surface the agent creates or edits.
test(): Promise<void>throw| Surface | Minimum phases |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| domain | definition() integrity; validate() behavior when present; examples() consistency when present |
| api | availability of _service or _composition; validation/authorization when present; integrated execution through handler() |
| ui | view() returns a valid visual unit; local slots function; basic integrated flow closes |
| web | view() returns a valid web visual unit; local slots function; basic integrated flow closes |
| mobile | view() returns a valid mobile visual unit; local slots function; basic integrated flow closes |
| stream | subscribe() shape when present; pipeline slots function; handler() processes a valid synthetic event |
| agentic | definition integrity; schema and policy consistency;tool.execute() delegates and returns expected shape |
When apps/agent/ is touched, validation must also cover:
AgenticRegistry lookup and catalog behaviorAgenticContext materializationinitialize, tools/list, tools/callinitializeMcp()stdio exists, stdout remains reserved for protocol messages and startup guidance keeps stdio separate from aggregate dev runnersIf the platform supports subagents, delegation, or parallel work:
If the platform does not support subagents:
Use subagents for:
Do not use subagents for:
Not every project will be fully aligned with APP.
| Level | Interpretation |
| ------------------- | ----------------------------------------------------- |
| structural-only | recognizable structure without explicit APP |
| partial | APP partially adopted or mixed with local conventions |
| full | explicit and consistent APP |
Rules:
handler()domainrouter() or subscribe() as business slotstest() when creating or editing a surface<case>.us.md when semantics change| If... | Then... |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| new capability | create <case>.us.md and domain first |
| new surface | update <case>.us.md before or during implementation |
| technical bug without semantic change | may fix directly, but still review test() |
| contract change | update <case>.us.md and test() |
| composition doubt | prefer atomic first; promote to composed only if needed |
| agentic surface is required | fully define discovery, context, prompt, tool, and test, plus any required policy / mcp / rag / examples |
| apps/agent/ is required | fully define the formal agent host contract, not only the Case-level agentic surfaces |
| platform supports subagents | use them when useful parallelism exists |
| platform does not support subagents | ignore that capability |
| automated tooling does not exist | validate manually with this skill checklist |
Before closing, confirm:
<case>.us.md was created or updated when requiredtest() was created or updated on touched surfacesThese templates preserve low-frequency operational memory content without keeping it in the critical path of the skill.
- Last inspection:
- Domains found:
- Cases per domain:
- Host apps:
- Current operational level:
- Structural observations:
- [CONV-001] Confirmed convention.
- [DEC-001] Confirmed decision.
- [LEARN-001] Correction or observed pattern.
development
use when setting up APP projects, adding host apps, creating or updating Cases, introducing packages, classifying shared code across cases/packages/core/shared, maintaining case .us.md artifacts, validating APP grammar, reviewing structural drift, and adapting existing projects incrementally with the canonical /app workflow
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.