@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 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.
@@ -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
@@ -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
+ }