devmethod-ai 0.1.0-rc.2 → 0.1.0
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/.agents/skills/decision-architecture/SKILL.md +15 -13
- package/.agents/skills/decision-architecture/assets/ADR.md +18 -17
- package/.agents/skills/decision-architecture/references/api-contracts.md +21 -16
- package/.agents/skills/decision-architecture/references/backend-boundaries.md +21 -18
- package/.agents/skills/decision-architecture/references/product-decisions.md +14 -17
- package/.agents/skills/design-to-code/SKILL.md +11 -10
- package/.agents/skills/design-to-code/assets/UI_ACCEPTANCE.md +13 -12
- package/.agents/skills/design-to-code/references/ux-contract.md +20 -15
- package/.agents/skills/project-foundation/SKILL.md +39 -30
- package/.agents/skills/project-foundation/assets/AGENTS.foundation.md +16 -15
- package/.agents/skills/project-foundation/assets/PROJECT_PROFILE.md +23 -23
- package/.agents/skills/project-foundation/assets/START_HERE.md +17 -14
- package/.agents/skills/project-foundation/references/mission-context.md +20 -0
- package/.agents/skills/project-foundation/references/operating-commands.md +37 -34
- package/.agents/skills/project-foundation/references/profiles/README.md +18 -0
- package/.agents/skills/project-foundation/references/profiles/ci.md +11 -0
- package/.agents/skills/project-foundation/references/profiles/cloud-delivery.md +13 -0
- package/.agents/skills/project-foundation/references/profiles/messaging.md +11 -0
- package/.agents/skills/project-foundation/references/profiles/mongodb.md +11 -0
- package/.agents/skills/project-foundation/references/profiles/node-nest.md +11 -0
- package/.agents/skills/project-foundation/references/profiles/postgres-drizzle.md +11 -0
- package/.agents/skills/project-foundation/references/profiles/react-next.md +11 -0
- package/.agents/skills/project-foundation/references/profiles/typescript.md +11 -0
- package/.agents/skills/project-foundation/references/work-sizing.md +31 -0
- package/.agents/skills/react-feature-engineering/SKILL.md +26 -22
- package/.agents/skills/react-feature-engineering/references/review-and-sources.md +41 -37
- package/.agents/skills/reliable-ai-integration/SKILL.md +13 -10
- package/.agents/skills/reliable-ai-integration/assets/AI_EVALUATION.md +19 -18
- package/.agents/skills/reliable-ai-integration/references/evidence-and-media.md +22 -18
- package/.agents/skills/reliable-ai-integration/references/jobs-and-costs.md +20 -15
- package/.agents/skills/scoped-delivery/SKILL.md +23 -18
- package/.agents/skills/scoped-delivery/assets/CHECKPOINT.md +22 -11
- package/.agents/skills/scoped-delivery/assets/MISSION.md +18 -0
- package/.agents/skills/scoped-delivery/assets/SLICE.md +15 -14
- package/.agents/skills/scoped-delivery/assets/VERIFICATION.md +19 -0
- package/.agents/skills/scoped-delivery/references/verification-and-cost.md +21 -18
- package/COMPATIBILITY.md +3 -3
- package/CONTRIBUTING.md +13 -0
- package/README.md +74 -8
- package/START_HERE.md +24 -0
- package/VALIDATION.md +37 -0
- package/dist/checkpoint.js +171 -0
- package/dist/cli.js +118 -17
- package/dist/doctor.js +113 -0
- package/dist/filesystem.js +32 -0
- package/dist/init.js +38 -34
- package/dist/mission.js +85 -0
- package/dist/planner.js +65 -0
- package/dist/records.js +76 -0
- package/dist/update.js +57 -0
- package/docs/ADR-001-distribution.md +13 -0
- package/docs/ADR-002-read-only-diagnostics.md +25 -0
- package/docs/ADR-003-installation-provenance.md +15 -0
- package/docs/ADR-004-checkpoint-resumption.md +23 -0
- package/docs/ADR-005-mission-context.md +11 -0
- package/docs/ADR-006-opt-in-native-pilot.md +15 -0
- package/docs/BMAD-COMPARISON.md +34 -0
- package/docs/CODEX-ADAPTER-VALIDATION.md +20 -0
- package/docs/COMPARISON-ITERATION.md +25 -0
- package/docs/DOCTOR.md +23 -0
- package/docs/EVALUATION.md +39 -0
- package/docs/EVOLUTION.md +34 -0
- package/docs/GREENFIELD-VALIDATION.md +36 -0
- package/docs/MILESTONES.md +39 -0
- package/docs/MISSIONS.md +45 -0
- package/docs/NATIVE-PILOT-RESULTS.md +22 -0
- package/docs/NATIVE-PILOT.md +17 -0
- package/docs/ORCHESTRATION.md +11 -0
- package/docs/RC2-VALIDATION.md +50 -0
- package/docs/RELEASE-0.1.0.md +49 -0
- package/docs/RELEASE-CHECKLIST.md +13 -0
- package/docs/RELEASE-NOTES-0.1.0-rc.2.md +32 -0
- package/docs/RELEASE-NOTES-0.1.0.md +11 -0
- package/docs/RESUMPTION.md +87 -0
- package/docs/ROADMAP.md +37 -0
- package/docs/STACK-PROFILES.md +9 -0
- package/docs/TROUBLESHOOTING.md +16 -0
- package/docs/UPDATES.md +30 -0
- package/docs/images/devmethod-flow.svg +26 -0
- package/evaluation/COMPARISONS.md +33 -0
- package/evaluation/README.md +35 -0
- package/evaluation/candidate-setup.json +33 -0
- package/evaluation/comparison-v2-readiness.json +18 -0
- package/evaluation/comparison-v2-results.json +58 -0
- package/evaluation/fixtures/dependency/CONTRIBUTING.md +3 -0
- package/evaluation/fixtures/dependency/TICKET.md +3 -0
- package/evaluation/fixtures/dependency/acceptance.test.mjs +4 -0
- package/evaluation/fixtures/dependency/api.mjs +1 -0
- package/evaluation/fixtures/failing-verification/CONTRIBUTING.md +3 -0
- package/evaluation/fixtures/failing-verification/acceptance.test.mjs +4 -0
- package/evaluation/fixtures/failing-verification/total.mjs +1 -0
- package/evaluation/fixtures/stale-handoff/CHECKPOINT.md +3 -0
- package/evaluation/fixtures/stale-handoff/CONTRACT.md +3 -0
- package/evaluation/fixtures/stale-handoff/CONTRIBUTING.md +3 -0
- package/evaluation/fixtures/stale-handoff/acceptance.test.mjs +4 -0
- package/evaluation/fixtures/stale-handoff/label.mjs +1 -0
- package/evaluation/fixtures/update-customization/README.md +13 -0
- package/evaluation/fixtures.json +85 -0
- package/evaluation/greenfield/acceptance.test.mjs +57 -0
- package/evaluation/greenfield/security.test.mjs +28 -0
- package/evaluation/native-pilot-results.json +155 -0
- package/examples/README.md +36 -0
- package/examples/bugfix/acceptance.test.mjs +17 -0
- package/examples/bugfix/page-size.mjs +4 -0
- package/examples/fullstack/CONTRACT.md +23 -0
- package/examples/fullstack/DEPENDENCIES.md +18 -0
- package/examples/fullstack/README.md +100 -0
- package/examples/fullstack/api/application/tasks.ts +13 -0
- package/examples/fullstack/api/domain/task.ts +12 -0
- package/examples/fullstack/api/http.ts +26 -0
- package/examples/fullstack/api/infrastructure/migrate.ts +15 -0
- package/examples/fullstack/api/infrastructure/postgres.ts +18 -0
- package/examples/fullstack/api/infrastructure/schema.ts +6 -0
- package/examples/fullstack/api/main.ts +17 -0
- package/examples/fullstack/api/tsconfig.json +17 -0
- package/examples/fullstack/compose.yaml +14 -0
- package/examples/fullstack/migrations/0000_tasks.sql +5 -0
- package/examples/fullstack/migrations/meta/_journal.json +13 -0
- package/examples/fullstack/mission.json +34 -0
- package/examples/fullstack/package-lock.json +2661 -0
- package/examples/fullstack/package.json +45 -0
- package/examples/fullstack/tests/e2e.test.cjs +54 -0
- package/examples/fullstack/tests/http.test.cjs +33 -0
- package/examples/fullstack/tests/postgres.test.cjs +29 -0
- package/examples/fullstack/tests/unit.test.cjs +20 -0
- package/examples/fullstack/tests/web-model.test.cjs +23 -0
- package/examples/fullstack/web/app/layout.tsx +4 -0
- package/examples/fullstack/web/app/page.tsx +11 -0
- package/examples/fullstack/web/features/tasks/components/task-list.tsx +11 -0
- package/examples/fullstack/web/features/tasks/hooks/use-task-filter.ts +7 -0
- package/examples/fullstack/web/features/tasks/model/tasks.ts +12 -0
- package/examples/fullstack/web/features/tasks/server/list-tasks.ts +8 -0
- package/examples/fullstack/web/next.config.mjs +2 -0
- package/examples/fullstack/web/tsconfig.json +36 -0
- package/examples/mission/CONTRACT.md +3 -0
- package/examples/mission/mission.json +18 -0
- package/examples/mission/plan.json +11 -0
- package/examples/pocket-tasks/BRIEF.md +21 -0
- package/examples/pocket-tasks/PROJECT_PROFILE.md +19 -0
- package/examples/pocket-tasks/README.md +43 -0
- package/examples/pocket-tasks/docs/ADR-001.md +7 -0
- package/examples/pocket-tasks/docs/HANDOFF.md +16 -0
- package/examples/pocket-tasks/docs/MISSION.md +15 -0
- package/examples/pocket-tasks/docs/VERIFICATION.md +58 -0
- package/examples/pocket-tasks/package.json +1 -0
- package/examples/pocket-tasks/public/app.js +84 -0
- package/examples/pocket-tasks/public/index.html +12 -0
- package/examples/pocket-tasks/public/styles.css +1 -0
- package/examples/pocket-tasks/server.mjs +79 -0
- package/examples/pocket-tasks/src/domain.mjs +31 -0
- package/examples/pocket-tasks/src/store.mjs +49 -0
- package/examples/pocket-tasks/tests/app.test.mjs +132 -0
- package/examples/pocket-tasks/tests/ui-errors.test.mjs +49 -0
- package/package.json +15 -3
- package/scripts/check-codex-git.mjs +20 -0
- package/scripts/check-docs.mjs +14 -0
- package/scripts/check-native-sandbox.mjs +11 -0
- package/scripts/comparison-readiness.mjs +26 -0
- package/scripts/comparison.mjs +42 -0
- package/scripts/evaluation.mjs +64 -0
- package/scripts/fullstack-mission.mjs +30 -0
- package/scripts/hosts/checks.mjs +13 -0
- package/scripts/hosts/codex-meter.mjs +17 -0
- package/scripts/hosts/codex-rpc.mjs +44 -0
- package/scripts/hosts/codex-task.mjs +29 -0
- package/scripts/hosts/codex.mjs +28 -0
- package/scripts/native-host.mjs +55 -0
- package/scripts/package-smoke.mjs +64 -0
- package/scripts/probe-codex-children.mjs +41 -0
- package/scripts/run-comparison-v2.mjs +45 -0
- package/scripts/run-native-pilot.mjs +76 -0
- package/scripts/summarize-native-pilot.mjs +7 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Starter exercises
|
|
2
|
+
|
|
3
|
+
These are fictional tasks, not recorded agent successes. Keep generated application code in a disposable repository. The public npm candidate predates the new work-sizing reference; use a reviewed source checkout to evaluate that guidance.
|
|
4
|
+
|
|
5
|
+
## B1: runnable small bug fix
|
|
6
|
+
|
|
7
|
+
Copy `examples/bugfix/` into a new directory outside the DevMethod checkout. With Node.js 22+, run:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
node --test acceptance.test.mjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The two tests intentionally fail on the starting implementation. This is expected and separate from DevMethod's own `npm test`. Optionally initialize a local Git repository to record the baseline, then install the chosen method into that fixture directory. For DevMethod, run from the reviewed DevMethod checkout:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
node dist/cli.js init --tool codex --dest /path/to/fixture
|
|
17
|
+
node dist/cli.js doctor --dest /path/to/fixture
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Use this task in the host; select `project-foundation` with the host's invocation syntax when testing DevMethod:
|
|
21
|
+
|
|
22
|
+
> Fix parsePageSize in page-size.mjs. Accept positive safe integer numbers and strings containing only decimal digits after trimming. Cap valid values at 100. Return 20 for every other value; do not coerce objects. Preserve the export and acceptance.test.mjs. Make no dependency additions or unrelated changes. Run node --test acceptance.test.mjs, review the diff, and report the observed result. Delivery is local only. Finish when this scope is verified.
|
|
23
|
+
|
|
24
|
+
Expected artifacts: focused implementation diff, actual test output and concise outcome. No new product spec or architecture decision is needed. Keep the starting fixture unchanged in this repository so subsequent evaluations use the same input.
|
|
25
|
+
|
|
26
|
+
## Existing React feature prompt
|
|
27
|
+
|
|
28
|
+
Requires an existing application, approved UI reference and real project commands; it is not a bundled runnable fixture.
|
|
29
|
+
|
|
30
|
+
> Use project-foundation to add an empty state to [existing screen], following [approved reference]. Reuse the installed components and existing server-state mechanism. Keep request orchestration in the existing feature hook and pure transformations outside the view. Preserve loading/error behavior and keyboard accessibility. Inspect package.json and applicable instructions to choose actual checks; verify the relevant rendered states. Scope excludes restyling other screens, dependency upgrades and deployment. Ask only about unresolved behavior that affects implementation.
|
|
31
|
+
|
|
32
|
+
## AI integration prompt
|
|
33
|
+
|
|
34
|
+
Requires an existing application and an approved provider contract. Live provider calls need the project's existing authorization and budget.
|
|
35
|
+
|
|
36
|
+
> Use project-foundation for [ticket]: add a summary action using the already approved provider behind the existing backend boundary. Validate inputs and output shape, keep credentials server-side, and preserve the specified timeout, retry and quota policy. Treat document content as untrusted data. Do not claim generated summaries are factual without the ticket's evidence criteria. Test success, invalid output and provider failure using the existing test setup; report live-provider validation separately. Scope excludes choosing a new provider, buying services or deployment.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { parsePageSize } from './page-size.mjs';
|
|
4
|
+
|
|
5
|
+
test('accepts positive safe integers and caps them at 100', () => {
|
|
6
|
+
for (const [input, expected] of [[1, 1], [25, 25], [' 12 ', 12], ['005', 5], [100, 100], [150, 100], ['150', 100]]) {
|
|
7
|
+
assert.equal(parsePageSize(input), expected, String(input));
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('invalid inputs return the default 20 without coercing objects', () => {
|
|
12
|
+
for (const input of [undefined, null, '', ' ', 0, -1, 1.5, NaN, Infinity, true, false,
|
|
13
|
+
[], [2], {}, '1.5', '-2', '+2', '1e2', '0x10', 'Infinity', Number.MAX_SAFE_INTEGER + 1,
|
|
14
|
+
{ valueOf() { throw new Error('Must not coerce objects'); } }]) {
|
|
15
|
+
assert.equal(parsePageSize(input), 20);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Task slice contract
|
|
2
|
+
|
|
3
|
+
Status: implemented fixture contract, owned by the fullstack slice. This fictional local example demonstrates one feature; it is not a production service architecture recommendation.
|
|
4
|
+
|
|
5
|
+
| Boundary | Observable contract |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `POST /tasks` | JSON object with only `title`; normalize with JavaScript `trim`; require 1–120 UTF-16 code units and reject U+0000 anywhere in the title; generate UUID; return `201` and `{ id, title }` after insertion |
|
|
8
|
+
| Invalid creation | `400` for missing/non-string/empty/too-long title or U+0000, array body or unknown fields; no persistence call |
|
|
9
|
+
| `GET /tasks` | `200` with at most 100 `{ id, title }` rows sorted by title then ID using PostgreSQL ordering; no cursor/pagination |
|
|
10
|
+
| Infrastructure failure | `500` with generic Nest response; driver connection details are not exposed to callers |
|
|
11
|
+
| Next initial read | Server-only HTTP request with no cache and 3-second timeout; validate shape and omit unknown fields before sending props to the client |
|
|
12
|
+
| UI filter | Case-insensitive substring search in the currently loaded rows; trims search text; no mutation or fetch |
|
|
13
|
+
| API unavailable | Render a readable alert; this demonstration page returns HTML successfully and is not a health endpoint |
|
|
14
|
+
|
|
15
|
+
No authentication, authorization, tenant separation, updates, deletion, business uniqueness, idempotency keys, events or production deployment are in scope. The API, web commands and database bind to loopback. A repeated POST creates another task; do not automatically retry an ambiguous mutation. There is no third-party call or paid operation.
|
|
16
|
+
|
|
17
|
+
The domain owns title normalization and validation, including rejection of U+0000 before persistence because PostgreSQL text cannot store that character. `Tasks` is the use case and owns the consumer-defined `TaskStore` port. Nest handles transport, `PostgresTasks` handles Drizzle, and composition assembles them. The client model is a separately validated HTTP representation; no client import reaches API internals. `useTaskFilter` owns only query state and calls a pure transformation.
|
|
18
|
+
|
|
19
|
+
Persistence uses one SQL INSERT per creation, already atomic in PostgreSQL. UUID primary-key uniqueness, SQL nonempty-space checks and a varchar length limit provide database safeguards. JavaScript trims more whitespace and counts UTF-16 code units differently from PostgreSQL character length; the application remains authoritative for the full title rule. No cross-row invariant requires a multi-statement transaction in this scope.
|
|
20
|
+
|
|
21
|
+
Migration `0000_tasks.sql` creates the table; Drizzle's journal records application. Apply with `npm run db:migrate` against an isolated database. Replaying the migrator must not recreate the table. Do not edit an applied migration; add a forward migration. There is no tested production rollback/backup/restore policy. Destroying the local Compose fixture is the documented reset, never a production recovery procedure.
|
|
22
|
+
|
|
23
|
+
Acceptance evidence is mapped in [README](README.md). Code, schema/migration, versions, environment or this contract changing invalidates the affected result. A passing HTTP test with an in-memory store does not prove PostgreSQL behavior; the real database and end-to-end suites establish their own narrower claims.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Fixture dependency security decision
|
|
2
|
+
|
|
3
|
+
On 2026-09-13, `npm audit --json` reported three high-severity dependency entries: `multer`, `@nestjs/platform-express`, and `@nestjs/core`. The Nest entries propagated the vulnerable transitive multer dependency; they were not three independent application flaws. Nest platform-express 12.0.1 pins multer 2.2.0 in its published npm metadata.
|
|
4
|
+
|
|
5
|
+
The following maintainer advisories identify multer 2.3.0 as the patched release:
|
|
6
|
+
|
|
7
|
+
- [Crafted multipart field names](https://github.com/expressjs/multer/security/advisories/GHSA-wc9g-mqfw-jrwm).
|
|
8
|
+
- [File descriptor leak on aborted uploads](https://github.com/expressjs/multer/security/advisories/GHSA-qfvm-cv95-jqjf).
|
|
9
|
+
- [Async fileFilter size-limit bypass](https://github.com/expressjs/multer/security/advisories/GHSA-qvfw-j98x-7q72).
|
|
10
|
+
- [Oversized array indices in field names](https://github.com/expressjs/multer/security/advisories/GHSA-535w-7cp7-47q4).
|
|
11
|
+
|
|
12
|
+
Sources were consulted on 2026-09-13, together with `npm view @nestjs/platform-express@12.0.1 dependencies --json` and `npm view multer@2.3.0 engines version --json`. The published patched package supports this fixture's runtime range.
|
|
13
|
+
|
|
14
|
+
Applicability assessment: the fixture installs the adapter but configures no Multer middleware or Nest file-upload interceptor. Its task controller expects JSON; the HTTP suite checks that a multipart request is rejected before persistence. The current routes therefore do not activate the affected upload parser, based on source inspection and this boundary check. This is not a claim that the original dependency was safe for an application adding uploads.
|
|
15
|
+
|
|
16
|
+
The fixture applies a narrow npm override from `@nestjs/platform-express` to `multer: 2.3.0`, preserving the Nest/React/Next major versions. The lockfile records the patched resolution. Do not use an automatic forced framework downgrade suggested by audit. Remove the override when the deliberately selected Nest adapter version natively resolves a patched compatible multer; then regenerate the lockfile and rerun the relevant checks.
|
|
17
|
+
|
|
18
|
+
Validation for this change: a clean lockfile install, npm audit, domain/HTTP/web-model tests and the production Next/Nest/PostgreSQL end-to-end suite. Their actual results belong to the commit review and mission evidence. This fixture has no upload feature, so it does not claim validation of file upload behavior or a complete security audit. Audit results are time-bound and do not prove absence of all vulnerabilities.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Next.js / NestJS / PostgreSQL fixture
|
|
2
|
+
|
|
3
|
+
A fictional local task list demonstrates a complete server-rendered read path and a validated creation API. It includes an actual Next application, Nest HTTP server, strict TypeScript, a Drizzle adapter and a PostgreSQL migration. Dependencies and tests are isolated from DevMethod's offline CLI.
|
|
4
|
+
|
|
5
|
+
Read [the contract](CONTRACT.md) before changing the slice. The installed dependency versions are pinned in [package.json](package.json) and [package-lock.json](package-lock.json); PostgreSQL's image is pinned by digest in [compose.yaml](compose.yaml). Use the existing versions when evaluating a mission rather than installing current releases.
|
|
6
|
+
|
|
7
|
+
## Run from a clean checkout
|
|
8
|
+
|
|
9
|
+
Prerequisites: Node.js 22+ and npm, Docker with Compose and permission to bind local ports. The recorded run used Node 23.10.0, npm 10.9.2 and Docker 27.5.1 on macOS. That is observed fixture compatibility, not a recommendation to deploy that Node version. Install with a supported project runtime and record your own result.
|
|
10
|
+
|
|
11
|
+
From the DevMethod repository:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
cd examples/fullstack
|
|
15
|
+
npm ci --ignore-scripts
|
|
16
|
+
export NEXT_TELEMETRY_DISABLED=1
|
|
17
|
+
npm test
|
|
18
|
+
docker compose -p devmethod-fixture up -d --wait
|
|
19
|
+
export DATABASE_URL=postgresql://fixture:fixture-local-only@127.0.0.1:55439/tasks
|
|
20
|
+
npm run test:db
|
|
21
|
+
npm run test:e2e
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The password above is a deliberately public, local-only fixture credential, not a secret or production configuration. Use only a disposable database: these suites apply migrations and insert/delete their own test rows. `test:db` and `test:e2e` fail when `DATABASE_URL` is missing, rather than reporting a skipped test as passed.
|
|
25
|
+
|
|
26
|
+
`npm test` compiles the API and runs six domain/use-case/HTTP/web-model tests. `test:db` verifies real migrations, replay, durability and database constraints. `test:e2e` builds API and Next production output, starts both HTTP servers on ephemeral loopback ports, creates a task through Nest, verifies its title in Next HTML, and verifies the API-unavailable rendering. It cleans up its task and HTTP processes.
|
|
27
|
+
|
|
28
|
+
To explore the feature after the tests, run in one terminal:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm run db:migrate
|
|
32
|
+
npm run start:api
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
In a second terminal, still in this directory:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
export NEXT_TELEMETRY_DISABLED=1
|
|
39
|
+
npm run start:web
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
In a third terminal create a fictional task and open the page:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
curl --fail-with-body -X POST http://127.0.0.1:3101/tasks \
|
|
46
|
+
-H 'content-type: application/json' -d '{"title":"Review the mission contract"}'
|
|
47
|
+
curl --fail http://127.0.0.1:3101/tasks
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Open [the local task list](http://127.0.0.1:3100), type `review` into the labeled filter and verify only matching tasks remain. Clear the input and verify the list returns. This browser/hydration protocol is **not run** in the recorded evidence. Initial HTML and the pure filter function are tested separately. Creation is deliberately through HTTP; there is no creation form.
|
|
51
|
+
|
|
52
|
+
Stop the foreground API/web processes with Ctrl-C. Remove only this disposable Compose project's container, anonymous data volume and network when finished:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
docker compose -p devmethod-fixture down -v
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Evidence and limits
|
|
59
|
+
|
|
60
|
+
Observed on 2026-09-13 for the candidate source tree committed with this example; use `git log -1 -- examples/fullstack` to resolve its introducing revision. These are automated fixture checks, not native Codex/Claude/Cursor behavioral evaluations or an independent design review.
|
|
61
|
+
|
|
62
|
+
| Acceptance criterion | Change | Executed verification | Result |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| Reject invalid titles before persistence | Domain and use case | `npm test` | passed |
|
|
65
|
+
| Preserve HTTP status/shape and hide infrastructure details | Nest controller | `npm test` | passed |
|
|
66
|
+
| Normalize filter and validate server data | Web model | `npm test` | passed |
|
|
67
|
+
| Apply migration once, replay safely, enforce SQL constraints and persist across connections | Drizzle adapter / SQL migration | `npm run test:db` | passed |
|
|
68
|
+
| Render a task created through the real API and database | Next server boundary | `npm run test:e2e` including production build | passed |
|
|
69
|
+
| Show a useful unavailable state | Next page | `npm run test:e2e` | passed |
|
|
70
|
+
| Browser hydration, keyboard filtering and visual quality | Client view/hook | Manual protocol above | not run |
|
|
71
|
+
| Production auth, deployment, transaction concurrency, restore | Outside slice | None | not run |
|
|
72
|
+
|
|
73
|
+
The first sandboxed HTTP run was blocked by loopback permissions and was rerun with local server access. An occupied initial database port was changed to 55439 without touching the existing service. Two test-harness issues (nested driver error comparison and cleanup order) were corrected before the final passing run; they were not product compatibility failures. Commands need network access for initial npm/image installation; the DevMethod CLI's offline contract does not extend to fetching example dependencies.
|
|
74
|
+
|
|
75
|
+
For a revision-specific evaluation, record the exact commit, command, environment, exit code and output in your mission evidence outside this source fixture. Re-run affected checks when source, contract, lockfile or environment changes. Never turn this historical table into fresh evidence for a later edit.
|
|
76
|
+
|
|
77
|
+
## Troubleshooting
|
|
78
|
+
|
|
79
|
+
- Port 55439 occupied: set `FIXTURE_DB_PORT` to an unused local port before Compose, and change the port in `DATABASE_URL` to match. Do not stop another project's service.
|
|
80
|
+
- Database test blocked: verify Docker is running, Compose is healthy and the URL points to this disposable database. An unavailable database is not a passing persistence check.
|
|
81
|
+
- Page unavailable: verify API/database startup and `TASKS_API_URL` (defaults to `http://127.0.0.1:3101`). The web server never needs database credentials.
|
|
82
|
+
- Migration/schema mismatch: inspect migration history and create a forward migration; do not erase a real database to satisfy the fixture.
|
|
83
|
+
- Missing package dependencies: run `npm ci --ignore-scripts` here, not at the DevMethod root. `npm run build` emits ignored runtime files; neither installation nor tests deploy anything.
|
|
84
|
+
|
|
85
|
+
Official technical provenance is recorded in the [optional stack profiles](../../docs/STACK-PROFILES.md), consulted 2026-09-13. A linked third-party skill is not installed or validated by this example.
|
|
86
|
+
|
|
87
|
+
## Mission, context, evidence and resumption
|
|
88
|
+
|
|
89
|
+
After installing this fixture's dependencies, run from the DevMethod repository root:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
node dist/cli.js mission --dest examples/fullstack --mission mission.json --json
|
|
93
|
+
node scripts/fullstack-mission.mjs
|
|
94
|
+
node dist/cli.js context-check --dest examples/fullstack --context evidence/context.json --json
|
|
95
|
+
node dist/cli.js resume --dest examples/fullstack --checkpoint evidence/checkpoint.json --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The walkthrough actually runs `npm test`, records its output and pins its inputs, and saves ignored metadata/evidence. A successful unchanged run reports ready. It records AC-TITLE only; database/e2e checks remain separate evidence in the table above. Tests are deterministic fixture checks, not a native agent transcript. The script never deploys, runs models or modifies a contract.
|
|
99
|
+
|
|
100
|
+
To exercise stale resumption in a disposable checkout, edit CONTRACT.md after the walkthrough and inspect resume again: source-changed and reverify must appear. Restore the exact bytes to return to the pinned content, or inspect the new contract and rerun affected verification. A branch change also requires reassessment; an unresolved dependency should remain in blockers until explicitly resolved. Completed scope sets nextAction to null and supplies no new task. Root mission/checkpoint tests reproduce these cases without mutating this example. If saving evidence itself changes Git status, restore the fixture's evidence/ ignore entry before recapturing context.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { Task, taskTitle } from '../domain/task';
|
|
3
|
+
export interface TaskStore {
|
|
4
|
+
list(): Promise<Task[]>;
|
|
5
|
+
insert(task: Task): Promise<Task>;
|
|
6
|
+
}
|
|
7
|
+
export class Tasks {
|
|
8
|
+
constructor(private readonly store: TaskStore) {}
|
|
9
|
+
list(): Promise<Task[]> { return this.store.list(); }
|
|
10
|
+
create(title: unknown): Promise<Task> {
|
|
11
|
+
return this.store.insert({ id: randomUUID(), title: taskTitle(title) });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const MAX_TITLE_LENGTH = 120;
|
|
2
|
+
export interface Task { id: string; title: string }
|
|
3
|
+
export class InvalidTitle extends Error {}
|
|
4
|
+
export function taskTitle(value: unknown): string {
|
|
5
|
+
if (typeof value !== 'string') throw new InvalidTitle('Title must be a string.');
|
|
6
|
+
if (value.includes('\u0000')) throw new InvalidTitle('Title must not contain a null character.');
|
|
7
|
+
const title = value.trim();
|
|
8
|
+
if (!title || title.length > MAX_TITLE_LENGTH) {
|
|
9
|
+
throw new InvalidTitle(`Title must contain 1–${MAX_TITLE_LENGTH} characters after trimming.`);
|
|
10
|
+
}
|
|
11
|
+
return title;
|
|
12
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { BadRequestException, Body, Controller, Get, Inject, Module, Post } from '@nestjs/common';
|
|
3
|
+
import { NestFactory } from '@nestjs/core';
|
|
4
|
+
import { Tasks, TaskStore } from './application/tasks';
|
|
5
|
+
import { InvalidTitle } from './domain/task';
|
|
6
|
+
@Controller('tasks')
|
|
7
|
+
class TasksController {
|
|
8
|
+
constructor(@Inject(Tasks) private readonly tasks: Tasks) {}
|
|
9
|
+
@Get() list() { return this.tasks.list(); }
|
|
10
|
+
@Post() async create(@Body() body: unknown) {
|
|
11
|
+
if (!body || typeof body !== 'object' || Array.isArray(body) ||
|
|
12
|
+
Object.keys(body).some((key) => key !== 'title')) {
|
|
13
|
+
throw new BadRequestException('Expected an object containing only title.');
|
|
14
|
+
}
|
|
15
|
+
try { return await this.tasks.create((body as { title?: unknown }).title); }
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error instanceof InvalidTitle) throw new BadRequestException(error.message);
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export async function createApp(store: TaskStore) {
|
|
23
|
+
@Module({ controllers: [TasksController], providers: [{ provide: Tasks, useValue: new Tasks(store) }] })
|
|
24
|
+
class AppModule {}
|
|
25
|
+
return NestFactory.create(AppModule, { logger: false });
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { Pool } from 'pg';
|
|
3
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
4
|
+
import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
|
5
|
+
export async function migrateDatabase(pool: Pool): Promise<void> {
|
|
6
|
+
await migrate(drizzle(pool), { migrationsFolder: resolve(__dirname, '../../migrations') });
|
|
7
|
+
}
|
|
8
|
+
if (require.main === module) {
|
|
9
|
+
if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is required.');
|
|
10
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
11
|
+
migrateDatabase(pool).finally(() => pool.end()).catch(() => {
|
|
12
|
+
console.error('Migration failed. Inspect the database locally; connection details are not logged.');
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { asc } from 'drizzle-orm';
|
|
2
|
+
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
3
|
+
import { Pool } from 'pg';
|
|
4
|
+
import { TaskStore } from '../application/tasks';
|
|
5
|
+
import { Task } from '../domain/task';
|
|
6
|
+
import { tasks } from './schema';
|
|
7
|
+
export class PostgresTasks implements TaskStore {
|
|
8
|
+
private readonly db;
|
|
9
|
+
constructor(pool: Pool) { this.db = drizzle(pool); }
|
|
10
|
+
list(): Promise<Task[]> {
|
|
11
|
+
return this.db.select().from(tasks).orderBy(asc(tasks.title), asc(tasks.id)).limit(100);
|
|
12
|
+
}
|
|
13
|
+
async insert(task: Task): Promise<Task> {
|
|
14
|
+
const [created] = await this.db.insert(tasks).values(task).returning();
|
|
15
|
+
if (!created) throw new Error('Insert returned no task.');
|
|
16
|
+
return created;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { check, pgTable, uuid, varchar } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
3
|
+
export const tasks = pgTable('tasks', {
|
|
4
|
+
id: uuid('id').primaryKey(),
|
|
5
|
+
title: varchar('title', { length: 120 }).notNull(),
|
|
6
|
+
}, (table) => [check('tasks_title_nonempty', sql`length(trim(${table.title})) > 0`)]);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Pool } from 'pg';
|
|
2
|
+
import { createApp } from './http';
|
|
3
|
+
import { PostgresTasks } from './infrastructure/postgres';
|
|
4
|
+
async function main() {
|
|
5
|
+
if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is required.');
|
|
6
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL, connectionTimeoutMillis: 3000 });
|
|
7
|
+
try {
|
|
8
|
+
await pool.query('select 1');
|
|
9
|
+
const app = await createApp(new PostgresTasks(pool));
|
|
10
|
+
try { await app.listen(Number(process.env.PORT ?? 3101), '127.0.0.1'); }
|
|
11
|
+
catch (error) { await app.close(); throw error; }
|
|
12
|
+
for (const signal of ['SIGINT', 'SIGTERM'] as const) {
|
|
13
|
+
process.once(signal, () => { void app.close().finally(() => pool.end()); });
|
|
14
|
+
}
|
|
15
|
+
} catch (error) { await pool.end(); throw error; }
|
|
16
|
+
}
|
|
17
|
+
main().catch(() => { console.error('API startup failed. Check local database configuration.'); process.exitCode = 1; });
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "Node",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"experimentalDecorators": true,
|
|
9
|
+
"emitDecoratorMetadata": true,
|
|
10
|
+
"outDir": "../build",
|
|
11
|
+
"rootDir": ".",
|
|
12
|
+
"skipLibCheck": true
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"**/*.ts"
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
services:
|
|
2
|
+
db:
|
|
3
|
+
image: postgres:17.6-alpine@sha256:ef257d85f76e48da1c64832459b59fcaba1a4dac97bf5d7450c77753542eee94
|
|
4
|
+
environment:
|
|
5
|
+
POSTGRES_USER: fixture
|
|
6
|
+
POSTGRES_PASSWORD: fixture-local-only
|
|
7
|
+
POSTGRES_DB: tasks
|
|
8
|
+
ports:
|
|
9
|
+
- "127.0.0.1:${FIXTURE_DB_PORT:-55439}:5432"
|
|
10
|
+
healthcheck:
|
|
11
|
+
test: ["CMD-SHELL", "pg_isready -U fixture -d tasks"]
|
|
12
|
+
interval: 2s
|
|
13
|
+
timeout: 2s
|
|
14
|
+
retries: 20
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"format": 1,
|
|
3
|
+
"id": "TASK-LIST",
|
|
4
|
+
"path": "standard",
|
|
5
|
+
"outcome": "Create validated tasks through Nest and render them in Next using PostgreSQL persistence.",
|
|
6
|
+
"scope": ["Task title validation", "Nest HTTP boundary", "Drizzle persistence", "Next read and filter boundaries"],
|
|
7
|
+
"exclusions": ["Authentication", "Deployment", "Creation form", "Broker integration"],
|
|
8
|
+
"invariants": ["Web cannot access database credentials", "Business validation precedes persistence", "Preserve the pinned framework versions"],
|
|
9
|
+
"uncertainties": ["Browser hydration requires a separate manual or browser check"],
|
|
10
|
+
"owner": "fixture-maintainer",
|
|
11
|
+
"status": "active",
|
|
12
|
+
"nextAction": "Review recorded unit evidence and run database/e2e checks under the documented local fixture contract.",
|
|
13
|
+
"stopConditions": ["Acceptance checks are recorded with current evidence", "A required dependency is unavailable"],
|
|
14
|
+
"acceptance": [
|
|
15
|
+
{"id": "AC-TITLE", "description": "Invalid titles fail before persistence", "changes": ["api/domain/task.ts", "api/http.ts"], "verification": "npm test", "kind": "automated"},
|
|
16
|
+
{"id": "AC-STORE", "description": "Tasks persist across PostgreSQL connections", "changes": ["api/infrastructure/postgres.ts", "migrations/0000_tasks.sql"], "verification": "npm run test:db", "kind": "automated"},
|
|
17
|
+
{"id": "AC-WEB", "description": "Next renders a task created through the real API and reports unavailability", "changes": ["web/app/page.tsx", "web/features/tasks/server/list-tasks.ts"], "verification": "npm run test:e2e", "kind": "automated"}
|
|
18
|
+
],
|
|
19
|
+
"sources": [
|
|
20
|
+
{"id": "runtime", "path": "package.json", "level": "project", "reason": "Pins versions and commands", "authority": "Executable fixture configuration", "kind": "fact", "revision": "working-tree"},
|
|
21
|
+
{"id": "lock", "path": "package-lock.json", "level": "project", "reason": "Pins resolved dependencies", "authority": "Dependency resolution for this fixture", "kind": "fact", "revision": "working-tree"},
|
|
22
|
+
{"id": "contract", "path": "CONTRACT.md", "level": "domain", "reason": "Defines title and HTTP semantics", "authority": "Accepted fictional fixture contract", "kind": "accepted-decision", "revision": "working-tree"},
|
|
23
|
+
{"id": "domain", "path": "api/domain/task.ts", "level": "domain", "reason": "Implements title validation", "authority": "Observed implementation, subordinate to accepted contract", "kind": "fact", "revision": "working-tree"},
|
|
24
|
+
{"id": "application", "path": "api/application/tasks.ts", "level": "domain", "reason": "Enforces use-case boundaries", "authority": "Observed implementation", "kind": "fact", "revision": "working-tree"},
|
|
25
|
+
{"id": "http", "path": "api/http.ts", "level": "mission", "reason": "Implements the HTTP acceptance boundary", "authority": "Observed HTTP implementation", "kind": "fact", "revision": "working-tree"},
|
|
26
|
+
{"id": "api-types", "path": "api/tsconfig.json", "level": "project", "reason": "Controls compiled runtime semantics", "authority": "Compiler configuration", "kind": "fact", "revision": "working-tree"},
|
|
27
|
+
{"id": "web-model", "path": "web/features/tasks/model/tasks.ts", "level": "mission", "reason": "Validates and filters task data", "authority": "Observed pure model", "kind": "fact", "revision": "working-tree"},
|
|
28
|
+
{"id": "unit-tests", "path": "tests/unit.test.cjs", "level": "execution", "reason": "Defines title acceptance checks", "authority": "Executable check, not a recorded outcome", "kind": "fact", "revision": "working-tree"},
|
|
29
|
+
{"id": "http-tests", "path": "tests/http.test.cjs", "level": "execution", "reason": "Defines HTTP acceptance checks", "authority": "Executable check, not a recorded outcome", "kind": "fact", "revision": "working-tree"},
|
|
30
|
+
{"id": "web-tests", "path": "tests/web-model.test.cjs", "level": "execution", "reason": "Defines pure model checks", "authority": "Executable check, not a recorded outcome", "kind": "fact", "revision": "working-tree"}
|
|
31
|
+
],
|
|
32
|
+
"dependencies": [],
|
|
33
|
+
"contradictions": []
|
|
34
|
+
}
|