@alistigo/features 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +33 -0
- package/dist/tags.d.ts +42 -0
- package/dist/tags.d.ts.map +1 -0
- package/dist/tags.js +53 -0
- package/dist/tags.js.map +1 -0
- package/features/core/README.md +36 -0
- package/features/core/add-element.feature +33 -0
- package/features/core/delete-element.feature +35 -0
- package/features/core/display-list.feature +37 -0
- package/features/core/persist.feature +27 -0
- package/package.json +32 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @alistigo/features
|
|
2
|
+
|
|
3
|
+
Behavior specifications for Alistigo AI, written in [Gherkin](https://cucumber.io/docs/gherkin/reference/).
|
|
4
|
+
|
|
5
|
+
These `.feature` files are the **source of truth for what the app should do**. The runner ([`@alistigo/features-runner-playwright`](../../cli/alistigo-features-runner-playwright/)) reads them and asserts the app conforms. We follow TDD — features are written and reviewed *before* the code that satisfies them.
|
|
6
|
+
|
|
7
|
+
The package follows the universal conventions of the [`gherkin-features` skill](../../ai/skills/gherkin-features/SKILL.md). The skill is the source of truth for the structure (organization, tooling, tag categories, glossary sections); this package fills in the Alistigo-specific contents.
|
|
8
|
+
|
|
9
|
+
## What lives here
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
packages/alistigo-features/
|
|
13
|
+
├── features/ # the .feature files (the actual specs)
|
|
14
|
+
│ └── core/ # one folder per FEATURE GROUP (mirrors @core tag)
|
|
15
|
+
├── src/
|
|
16
|
+
│ ├── index.ts # exports FEATURES_DIR + tag types
|
|
17
|
+
│ └── tags.ts # the typed tag taxonomy (Milestone / Group / Capability / …)
|
|
18
|
+
├── tools/
|
|
19
|
+
│ └── validate.ts # structural validator (taxonomy + parse + folder/group parity)
|
|
20
|
+
├── docs/
|
|
21
|
+
│ ├── organization.md # how features are organized
|
|
22
|
+
│ ├── style-guide.md # how to write a feature
|
|
23
|
+
│ ├── tags.md # the tag taxonomy with descriptions
|
|
24
|
+
│ └── glossary.md # ubiquitous language: Entities / Actors / Actions
|
|
25
|
+
├── .prettierrc.json # Prettier (with prettier-plugin-gherkin)
|
|
26
|
+
├── gherklin.config.ts # Gherkin lint rules
|
|
27
|
+
├── biome.json # Biome config (extends root, ignores .feature)
|
|
28
|
+
├── package.json
|
|
29
|
+
└── project.json # Nx targets
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Tooling
|
|
33
|
+
|
|
34
|
+
| Tool | What it does | When it runs |
|
|
35
|
+
|------|--------------|--------------|
|
|
36
|
+
| [Prettier](https://prettier.io/) + [`prettier-plugin-gherkin`](https://www.npmjs.com/package/prettier-plugin-gherkin) | Formats `.feature` files (indentation, alignment, tag placement) | `pnpm format` / on save |
|
|
37
|
+
| [Gherklin](https://github.com/cjmarkham/Gherklin) | Lints `.feature` files (structural rules, naming, duplicates, tag presence) | `pnpm nx qa:lint` |
|
|
38
|
+
| `tools/validate.ts` (custom) | Parses every feature with `@cucumber/gherkin` and checks the **tag taxonomy** is respected | `pnpm validate` |
|
|
39
|
+
| [Biome](https://biomejs.dev/) | Lints/formats TS/JS in this package only (ignores `features/`) | repo standard |
|
|
40
|
+
|
|
41
|
+
Why this combination:
|
|
42
|
+
- **Biome doesn't speak Gherkin** — Prettier + the gherkin plugin is the most-maintained option in 2026.
|
|
43
|
+
- **Gherklin > older `gherkin-lint`** — modern TS/ESM, extensible, actively maintained.
|
|
44
|
+
- The custom validator is a tiny TypeScript script that catches what the linter can't: tags that exist in files but aren't in the typed taxonomy. This keeps `src/tags.ts` and the actual `.feature` files in sync.
|
|
45
|
+
|
|
46
|
+
## Conventions, in three places
|
|
47
|
+
|
|
48
|
+
| If you want to know… | Read this |
|
|
49
|
+
|----------------------|-----------|
|
|
50
|
+
| **How features and folders are organized** | [docs/organization.md](docs/organization.md) |
|
|
51
|
+
| **How to write a good Gherkin scenario** | [docs/style-guide.md](docs/style-guide.md) |
|
|
52
|
+
| **What tags exist and what they mean** | [docs/tags.md](docs/tags.md) |
|
|
53
|
+
| **What domain words mean (ubiquitous language)** | [docs/glossary.md](docs/glossary.md) |
|
|
54
|
+
|
|
55
|
+
The tag taxonomy is also typed in [`src/tags.ts`](src/tags.ts). When you add or rename a tag, update *both* files — the validator enforces it.
|
|
56
|
+
|
|
57
|
+
## Common Commands
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
pnpm format # format all .feature files with Prettier
|
|
61
|
+
pnpm format:check # check formatting without writing
|
|
62
|
+
pnpm nx qa:lint # Biome (TS) + Gherklin (.feature)
|
|
63
|
+
pnpm nx qa:lint:gherkin # Gherklin only
|
|
64
|
+
pnpm validate # parse every .feature and check the tag taxonomy
|
|
65
|
+
pnpm typecheck # TypeScript check on src/ and tools/
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or via Nx from the repo root:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
nx run alistigo-features:lint
|
|
72
|
+
nx run alistigo-features:format
|
|
73
|
+
nx run alistigo-features:validate
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Programmatic access
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { FEATURES_DIR, MILESTONE_TAGS, isKnownTag } from "@alistigo/features";
|
|
80
|
+
|
|
81
|
+
// FEATURES_DIR is an absolute path — pass it to a runner.
|
|
82
|
+
console.log(FEATURES_DIR);
|
|
83
|
+
|
|
84
|
+
// MILESTONE_TAGS, CAPABILITY_TAGS, TEST_TYPE_TAGS, SUITE_TAGS, ACTOR_TAGS exported.
|
|
85
|
+
console.log(MILESTONE_TAGS); // ['@m1', '@m2', '@m3', '@m4', '@v1']
|
|
86
|
+
|
|
87
|
+
// Validate a tag at runtime.
|
|
88
|
+
isKnownTag("@m1"); // true
|
|
89
|
+
isKnownTag("@made-up"); // false
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Contributing a feature
|
|
93
|
+
|
|
94
|
+
See [docs/organization.md](docs/organization.md) and [docs/style-guide.md](docs/style-guide.md). In short:
|
|
95
|
+
|
|
96
|
+
1. Pick the group folder (`features/core/`, or a plugin folder once those land).
|
|
97
|
+
2. Name the file after the **capability** it covers, kebab-case (`add-element.feature`, `display-list.feature`).
|
|
98
|
+
3. Tag the `Feature:` with exactly one milestone tag (`@m1` …), exactly one group tag matching the folder (`@core`), and one or more capability tags (`@capability:element` …).
|
|
99
|
+
4. Use only words from [docs/glossary.md](docs/glossary.md) in step text.
|
|
100
|
+
5. Write declarative scenarios (business language, not UI selectors).
|
|
101
|
+
6. Run `pnpm format && pnpm nx qa:lint && pnpm validate` before committing.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @alistigo/features
|
|
3
|
+
*
|
|
4
|
+
* Public surface of the package. Consumers (the runner, CI, anything that
|
|
5
|
+
* needs to enumerate or filter feature scenarios) import from here.
|
|
6
|
+
*/
|
|
7
|
+
export * from "./tags.js";
|
|
8
|
+
/**
|
|
9
|
+
* Absolute path to the directory containing all `.feature` files in this
|
|
10
|
+
* package. Resolved relative to the published `dist/` so it works after build.
|
|
11
|
+
*/
|
|
12
|
+
export declare const FEATURES_DIR: string;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,cAAc,WAAW,CAAC;AAE1B;;;GAGG;AACH,eAAO,MAAM,YAAY,QAIxB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @alistigo/features
|
|
3
|
+
*
|
|
4
|
+
* Public surface of the package. Consumers (the runner, CI, anything that
|
|
5
|
+
* needs to enumerate or filter feature scenarios) import from here.
|
|
6
|
+
*/
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
export * from "./tags.js";
|
|
10
|
+
/**
|
|
11
|
+
* Absolute path to the directory containing all `.feature` files in this
|
|
12
|
+
* package. Resolved relative to the published `dist/` so it works after build.
|
|
13
|
+
*/
|
|
14
|
+
export const FEATURES_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "features");
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,cAAc,WAAW,CAAC;AAE1B;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CACtC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAC5C,IAAI,EACJ,UAAU,CACX,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alistigo/features",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Gherkin behavior specifications for Alistigo AI. Source of truth for what the app should do; the runner reads these files to assert conformance.",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./tags": {
|
|
13
|
+
"import": "./dist/tags.js",
|
|
14
|
+
"types": "./dist/tags.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"features"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"gherklin": "^1.0.0",
|
|
29
|
+
"prettier": "^3.3.0",
|
|
30
|
+
"prettier-plugin-gherkin": "^3.1.2"
|
|
31
|
+
},
|
|
32
|
+
"module": "./index.js"
|
|
33
|
+
}
|
package/dist/tags.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag taxonomy for Alistigo feature files.
|
|
3
|
+
*
|
|
4
|
+
* This file is the **source of truth** for every tag the codebase recognizes.
|
|
5
|
+
* The Gherklin lint config references it; the runner uses it to filter
|
|
6
|
+
* scenarios; CI uses it to assemble suite runs.
|
|
7
|
+
*
|
|
8
|
+
* When you introduce a new tag, add it here AND document it in `docs/tags.md`.
|
|
9
|
+
* Untyped or undocumented tags are rejected by the lint pipeline.
|
|
10
|
+
*
|
|
11
|
+
* Tag categories follow the `gherkin-features` skill: Milestone, Group,
|
|
12
|
+
* Capability, Test type, Suite, Actor.
|
|
13
|
+
*/
|
|
14
|
+
/** Which milestone a feature belongs to. Required — every Feature has exactly one. */
|
|
15
|
+
export declare const MILESTONE_TAGS: readonly ["@m1", "@m2", "@m3", "@m4", "@v1"];
|
|
16
|
+
export type MilestoneTag = (typeof MILESTONE_TAGS)[number];
|
|
17
|
+
/**
|
|
18
|
+
* Which feature group a feature belongs to. Required — every Feature has
|
|
19
|
+
* exactly one. Mirrors the folder layout under `features/`.
|
|
20
|
+
*
|
|
21
|
+
* - `@core` — base list app (text elements, add, delete, persist)
|
|
22
|
+
* - Plugin groups will be added as plugins land: `@todo`, `@checklist`, etc.
|
|
23
|
+
*/
|
|
24
|
+
export declare const GROUP_TAGS: readonly ["@core"];
|
|
25
|
+
export type GroupTag = (typeof GROUP_TAGS)[number];
|
|
26
|
+
/** What capability the feature exercises. Required — at least one. */
|
|
27
|
+
export declare const CAPABILITY_TAGS: readonly ["@capability:list", "@capability:element", "@capability:persistence", "@capability:loading", "@capability:export", "@capability:host-protocol", "@capability:plugins"];
|
|
28
|
+
export type CapabilityTag = (typeof CAPABILITY_TAGS)[number];
|
|
29
|
+
/** What kind of scenario this is. Optional. */
|
|
30
|
+
export declare const TEST_TYPE_TAGS: readonly ["@happy-path", "@edge-case", "@error-path"];
|
|
31
|
+
export type TestTypeTag = (typeof TEST_TYPE_TAGS)[number];
|
|
32
|
+
/** Which suite the scenario participates in. Optional. */
|
|
33
|
+
export declare const SUITE_TAGS: readonly ["@smoke", "@regression", "@todo"];
|
|
34
|
+
export type SuiteTag = (typeof SUITE_TAGS)[number];
|
|
35
|
+
/** Who triggers the scenario. Optional. */
|
|
36
|
+
export declare const ACTOR_TAGS: readonly ["@actor:user", "@actor:ai", "@actor:host"];
|
|
37
|
+
export type ActorTag = (typeof ACTOR_TAGS)[number];
|
|
38
|
+
export type AlistigoTag = MilestoneTag | GroupTag | CapabilityTag | TestTypeTag | SuiteTag | ActorTag;
|
|
39
|
+
export declare const ALL_TAGS: readonly AlistigoTag[];
|
|
40
|
+
/** True if the tag is part of the recognized taxonomy. */
|
|
41
|
+
export declare function isKnownTag(tag: string): tag is AlistigoTag;
|
|
42
|
+
//# sourceMappingURL=tags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,sFAAsF;AACtF,eAAO,MAAM,cAAc,8CAA+C,CAAC;AAC3E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,oBAAqB,CAAC;AAC7C,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,sEAAsE;AACtE,eAAO,MAAM,eAAe,kLAQlB,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,+CAA+C;AAC/C,eAAO,MAAM,cAAc,uDAAwD,CAAC;AACpF,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,0DAA0D;AAC1D,eAAO,MAAM,UAAU,6CAA8C,CAAC;AACtE,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,2CAA2C;AAC3C,eAAO,MAAM,UAAU,sDAAuD,CAAC;AAC/E,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,QAAQ,GACR,aAAa,GACb,WAAW,GACX,QAAQ,GACR,QAAQ,CAAC;AAEb,eAAO,MAAM,QAAQ,EAAE,SAAS,WAAW,EAO1C,CAAC;AAIF,0DAA0D;AAC1D,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,IAAI,WAAW,CAE1D"}
|
package/dist/tags.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tag taxonomy for Alistigo feature files.
|
|
3
|
+
*
|
|
4
|
+
* This file is the **source of truth** for every tag the codebase recognizes.
|
|
5
|
+
* The Gherklin lint config references it; the runner uses it to filter
|
|
6
|
+
* scenarios; CI uses it to assemble suite runs.
|
|
7
|
+
*
|
|
8
|
+
* When you introduce a new tag, add it here AND document it in `docs/tags.md`.
|
|
9
|
+
* Untyped or undocumented tags are rejected by the lint pipeline.
|
|
10
|
+
*
|
|
11
|
+
* Tag categories follow the `gherkin-features` skill: Milestone, Group,
|
|
12
|
+
* Capability, Test type, Suite, Actor.
|
|
13
|
+
*/
|
|
14
|
+
/** Which milestone a feature belongs to. Required — every Feature has exactly one. */
|
|
15
|
+
export const MILESTONE_TAGS = ["@m1", "@m2", "@m3", "@m4", "@v1"];
|
|
16
|
+
/**
|
|
17
|
+
* Which feature group a feature belongs to. Required — every Feature has
|
|
18
|
+
* exactly one. Mirrors the folder layout under `features/`.
|
|
19
|
+
*
|
|
20
|
+
* - `@core` — base list app (text elements, add, delete, persist)
|
|
21
|
+
* - Plugin groups will be added as plugins land: `@todo`, `@checklist`, etc.
|
|
22
|
+
*/
|
|
23
|
+
export const GROUP_TAGS = ["@core"];
|
|
24
|
+
/** What capability the feature exercises. Required — at least one. */
|
|
25
|
+
export const CAPABILITY_TAGS = [
|
|
26
|
+
"@capability:list",
|
|
27
|
+
"@capability:element",
|
|
28
|
+
"@capability:persistence",
|
|
29
|
+
"@capability:loading",
|
|
30
|
+
"@capability:export",
|
|
31
|
+
"@capability:host-protocol",
|
|
32
|
+
"@capability:plugins",
|
|
33
|
+
];
|
|
34
|
+
/** What kind of scenario this is. Optional. */
|
|
35
|
+
export const TEST_TYPE_TAGS = ["@happy-path", "@edge-case", "@error-path"];
|
|
36
|
+
/** Which suite the scenario participates in. Optional. */
|
|
37
|
+
export const SUITE_TAGS = ["@smoke", "@regression", "@todo"];
|
|
38
|
+
/** Who triggers the scenario. Optional. */
|
|
39
|
+
export const ACTOR_TAGS = ["@actor:user", "@actor:ai", "@actor:host"];
|
|
40
|
+
export const ALL_TAGS = [
|
|
41
|
+
...MILESTONE_TAGS,
|
|
42
|
+
...GROUP_TAGS,
|
|
43
|
+
...CAPABILITY_TAGS,
|
|
44
|
+
...TEST_TYPE_TAGS,
|
|
45
|
+
...SUITE_TAGS,
|
|
46
|
+
...ACTOR_TAGS,
|
|
47
|
+
];
|
|
48
|
+
const TAG_SET = new Set(ALL_TAGS);
|
|
49
|
+
/** True if the tag is part of the recognized taxonomy. */
|
|
50
|
+
export function isKnownTag(tag) {
|
|
51
|
+
return TAG_SET.has(tag);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=tags.js.map
|
package/dist/tags.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tags.js","sourceRoot":"","sources":["../src/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAG3E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,CAAU,CAAC;AAG7C,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,kBAAkB;IAClB,qBAAqB;IACrB,yBAAyB;IACzB,qBAAqB;IACrB,oBAAoB;IACpB,2BAA2B;IAC3B,qBAAqB;CACb,CAAC;AAGX,+CAA+C;AAC/C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,aAAa,EAAE,YAAY,EAAE,aAAa,CAAU,CAAC;AAGpF,0DAA0D;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,aAAa,EAAE,OAAO,CAAU,CAAC;AAGtE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,aAAa,CAAU,CAAC;AAW/E,MAAM,CAAC,MAAM,QAAQ,GAA2B;IAC9C,GAAG,cAAc;IACjB,GAAG,UAAU;IACb,GAAG,eAAe;IAClB,GAAG,cAAc;IACjB,GAAG,UAAU;IACb,GAAG,UAAU;CACd,CAAC;AAEF,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,QAAQ,CAAC,CAAC;AAE1C,0DAA0D;AAC1D,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# core — Base list app
|
|
2
|
+
|
|
3
|
+
The foundational feature group for Alistigo AI. Every list type the project will ever support (todo, checklist, grocery, wishlist, …) is built **on top of** the core list app via plugins.
|
|
4
|
+
|
|
5
|
+
These features describe what the **base list** does — without any plugin loaded.
|
|
6
|
+
|
|
7
|
+
| File | Capability |
|
|
8
|
+
|------|------------|
|
|
9
|
+
| [display-list.feature](display-list.feature) | Render the list (empty, populated, with duplicates) |
|
|
10
|
+
| [add-element.feature](add-element.feature) | Append a text element |
|
|
11
|
+
| [delete-element.feature](delete-element.feature) | Remove an element by identity (or by row when duplicates collide) |
|
|
12
|
+
| [persist.feature](persist.feature) | Survive a reload |
|
|
13
|
+
|
|
14
|
+
## Scope
|
|
15
|
+
|
|
16
|
+
The base list:
|
|
17
|
+
|
|
18
|
+
- holds a sequence of **text elements** — nothing else (no checkbox, no priority, no due date — those are plugin behaviors)
|
|
19
|
+
- has an **internal order** that is preserved on read but **NOT displayed** to the user (the user sees rows; positions are not shown)
|
|
20
|
+
- supports **add** and **delete**
|
|
21
|
+
- **allows duplicates** — two elements with the same text are two distinct elements with distinct identities
|
|
22
|
+
|
|
23
|
+
What's deliberately out of scope at this layer:
|
|
24
|
+
- Completion / checkbox state → todo plugin (later)
|
|
25
|
+
- Sorting, ordered display → ordering plugin (later)
|
|
26
|
+
- Editing element text → edit plugin (later)
|
|
27
|
+
- Categories, priorities, dates, images → respective plugins (later)
|
|
28
|
+
|
|
29
|
+
## Definition of Done
|
|
30
|
+
|
|
31
|
+
The base list (and its M1 milestone) is done when every scenario in this folder is green via the runner *and* replaying the event log produces the same projection.
|
|
32
|
+
|
|
33
|
+
See:
|
|
34
|
+
- [`projects/alistigo-ai/milestones.md`](../../../../projects/alistigo-ai/milestones.md) for the milestone roadmap
|
|
35
|
+
- [`docs/glossary.md`](../../docs/glossary.md) for the entities, actors, and actions that may appear in step text
|
|
36
|
+
- [`docs/style-guide.md`](../../docs/style-guide.md) for how to write a scenario
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@m1
|
|
2
|
+
@core
|
|
3
|
+
@capability:element
|
|
4
|
+
@actor:user
|
|
5
|
+
Feature: Add an element to a list
|
|
6
|
+
As a User
|
|
7
|
+
I want to add a text element to the list
|
|
8
|
+
So that I can capture something into it
|
|
9
|
+
|
|
10
|
+
@happy-path
|
|
11
|
+
@smoke
|
|
12
|
+
Scenario: Add to an empty list
|
|
13
|
+
Given an empty list
|
|
14
|
+
When I add "Buy bread"
|
|
15
|
+
Then the list should be:
|
|
16
|
+
| Buy bread |
|
|
17
|
+
|
|
18
|
+
@happy-path
|
|
19
|
+
Scenario: Add to a populated list
|
|
20
|
+
Given a list:
|
|
21
|
+
| Buy bread |
|
|
22
|
+
When I add "Call mom"
|
|
23
|
+
Then the list should be:
|
|
24
|
+
| Buy bread |
|
|
25
|
+
| Call mom |
|
|
26
|
+
|
|
27
|
+
Scenario: Add an element with the same text as an existing one
|
|
28
|
+
Given a list:
|
|
29
|
+
| Buy bread |
|
|
30
|
+
When I add "Buy bread"
|
|
31
|
+
Then the list should be:
|
|
32
|
+
| Buy bread |
|
|
33
|
+
| Buy bread |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
@m1
|
|
2
|
+
@core
|
|
3
|
+
@capability:element
|
|
4
|
+
@actor:user
|
|
5
|
+
Feature: Delete an element from a list
|
|
6
|
+
As a User
|
|
7
|
+
I want to remove an element
|
|
8
|
+
So that the list reflects only what I still care about
|
|
9
|
+
|
|
10
|
+
@happy-path
|
|
11
|
+
@smoke
|
|
12
|
+
Scenario: Delete the only element of a list
|
|
13
|
+
Given a list:
|
|
14
|
+
| Buy bread |
|
|
15
|
+
When I delete "Buy bread"
|
|
16
|
+
Then the list should be empty
|
|
17
|
+
|
|
18
|
+
@happy-path
|
|
19
|
+
Scenario: Delete from a populated list
|
|
20
|
+
Given a list:
|
|
21
|
+
| Buy bread |
|
|
22
|
+
| Call mom |
|
|
23
|
+
When I delete "Buy bread"
|
|
24
|
+
Then the list should be:
|
|
25
|
+
| Call mom |
|
|
26
|
+
|
|
27
|
+
Scenario: Delete one occurrence of a duplicate
|
|
28
|
+
Given a list:
|
|
29
|
+
| Buy bread |
|
|
30
|
+
| Call mom |
|
|
31
|
+
| Buy bread |
|
|
32
|
+
When I delete row 1
|
|
33
|
+
Then the list should be:
|
|
34
|
+
| Call mom |
|
|
35
|
+
| Buy bread |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
@m1
|
|
2
|
+
@core
|
|
3
|
+
@capability:list
|
|
4
|
+
@capability:loading
|
|
5
|
+
Feature: Display a list
|
|
6
|
+
As a User opening the list
|
|
7
|
+
I want to see all elements of the list
|
|
8
|
+
So that I know what is in it
|
|
9
|
+
|
|
10
|
+
@happy-path
|
|
11
|
+
@smoke
|
|
12
|
+
Scenario: An empty list shows an empty state
|
|
13
|
+
Given an empty list
|
|
14
|
+
When I open the list
|
|
15
|
+
Then the list should be empty
|
|
16
|
+
And an empty-state message should be visible
|
|
17
|
+
|
|
18
|
+
@happy-path
|
|
19
|
+
Scenario: A populated list shows every element
|
|
20
|
+
Given a list:
|
|
21
|
+
| Buy bread |
|
|
22
|
+
| Call mom |
|
|
23
|
+
When I open the list
|
|
24
|
+
Then the list should be:
|
|
25
|
+
| Buy bread |
|
|
26
|
+
| Call mom |
|
|
27
|
+
|
|
28
|
+
Scenario: Duplicate text appears as separate elements
|
|
29
|
+
Given a list:
|
|
30
|
+
| Buy bread |
|
|
31
|
+
| Call mom |
|
|
32
|
+
| Buy bread |
|
|
33
|
+
When I open the list
|
|
34
|
+
Then the list should be:
|
|
35
|
+
| Buy bread |
|
|
36
|
+
| Call mom |
|
|
37
|
+
| Buy bread |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
@m1
|
|
2
|
+
@core
|
|
3
|
+
@capability:persistence
|
|
4
|
+
Feature: Persist a list across reloads
|
|
5
|
+
As a User
|
|
6
|
+
I want my list to survive a reload
|
|
7
|
+
So that I do not lose its content when the list is reopened
|
|
8
|
+
|
|
9
|
+
@happy-path
|
|
10
|
+
@smoke
|
|
11
|
+
Scenario: An added element is still there after a reload
|
|
12
|
+
Given an empty list
|
|
13
|
+
When I add "Buy bread"
|
|
14
|
+
And I reload the list
|
|
15
|
+
Then the list should be:
|
|
16
|
+
| Buy bread |
|
|
17
|
+
|
|
18
|
+
@happy-path
|
|
19
|
+
@smoke
|
|
20
|
+
Scenario: A deleted element stays deleted after a reload
|
|
21
|
+
Given a list:
|
|
22
|
+
| Buy bread |
|
|
23
|
+
| Call mom |
|
|
24
|
+
When I delete "Buy bread"
|
|
25
|
+
And I reload the list
|
|
26
|
+
Then the list should be:
|
|
27
|
+
| Call mom |
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alistigo/features",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Gherkin behavior specifications for Alistigo AI. Source of truth for what the app should do; the runner reads these files to assert conformance.",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./tags": {
|
|
13
|
+
"import": "./dist/tags.js",
|
|
14
|
+
"types": "./dist/tags.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"features"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"gherklin": "^1.0.0",
|
|
29
|
+
"prettier": "^3.3.0",
|
|
30
|
+
"prettier-plugin-gherkin": "^3.1.2"
|
|
31
|
+
}
|
|
32
|
+
}
|