Skip to content

Infraestructura base: monorepo pnpm + TanStack Start + MongoDB en Docker + console-runner #1

Closed

Description

@brauliodiez

Summary

Memora no tiene todavía ni una línea de código de producto: esta tarea entrega el terreno de trabajo y nada de producto. Un monorepo pnpm + Turborepo cuyo package.json raíz es la única puerta de entrada; apps/web con TanStack Start + Tailwind + shadcn (new-york / neutral) mostrando un «Hola Mundo» que demuestra que la cadena de estilos aplica de punta a punta; MongoDB 8 en Docker Compose (sin credenciales, volumen nombrado, solo para local) con los scripts db:up / db:down / db:reset; packages/db como único sitio del repositorio que resuelve MONGODB_URI y construye el cliente de Mongo; y console-runner en la raíz (por petición explícita) con un menú interactivo de mantenimiento que comprueba la conexión, limpia la base de datos tras una confirmación explícita e imprime un TODO en la opción de datos de prueba — un hueco señalizado, nunca trabajo fingido. La calidad la sostienen tsc --noEmit y Prettier, sin linter por decisión explícita, con un workflow de GitHub Actions que ejecuta typecheck, format:check, test y build en cada PR para que el gate de merge del harness tenga un verde real que leer. El criterio de éxito es operativo: quien clona el repositorio levanta la base de datos, arranca el front y ejecuta el runner con comandos de la raíz — y eso se verifica a mano (db:up → runner → dev → la página carga), porque la suite de tests es hermética y no cubre la infra real.

Requirements — Infraestructura y estructura base del monorepo

Source PRD: docs/prds/infra-monorepo-2026-10-17.md
Task: #1 — branch harness/1-infra-monorepo

Scope note: this delivery is infrastructure only. No product surface, no domain
model, no auth. The success criterion is operational: a fresh clone can bring up the
database, start the front and run the maintenance runner from root package.json
scripts, and the PR passes the mechanical gates.

A. Monorepo root — one install, one entry point (US1)

  • R1 (ubiquitous): The repository root shall be a private pnpm workspace whose
    pnpm-workspace.yaml declares apps/*, packages/* and console-runner as
    workspace packages.
  • R2 (ubiquitous): The root package.json shall pin the package manager with
    packageManager: "pnpm@10.19.0" and declare the supported Node version in engines
    (node: ">=24.16.0 <25"), matched by a committed .nvmrc holding 24.16.0.
  • R3 (event): When pnpm install is run at the repository root, the system shall
    install and link every workspace package's dependencies in a single command, with no
    further manual configuration.
  • R4 (ubiquitous): After the migration the repository shall contain exactly one
    lockfile — pnpm-lock.yaml — and no package-lock.json.
  • R5 (ubiquitous): The harness CLI shall stay resolvable at
    <repo-root>/node_modules/.bin/lemony after a pnpm install, because
    .claude/hooks/lib/lemony.sh resolves that exact path.
  • R6 (ubiquitous): The root package.json shall be the only entry point and expose
    exactly these scripts: dev, build, test, typecheck, format, format:check,
    db:up, db:down, db:reset, runner.
  • R7 (event): When one of the workspace-spanning root scripts (dev, build,
    test, typecheck) is run, the system shall delegate the work to Turborepo through
    turbo.json, not to a hand-written loop over directories.
  • R8 (ubiquitous): Every workspace package shall compile under TypeScript strict
    mode (strict: true) from a shared root tsconfig.base.json.
  • R9 (event): When pnpm typecheck is run, the system shall run tsc --noEmit for
    every workspace and exit non-zero if any workspace reports a type error.
  • R10 (event): When pnpm format:check is run, the system shall verify Prettier
    formatting across the project's own tracked files and exit non-zero if any file is
    unformatted.
  • R11 (ubiquitous): The Prettier scope shall exclude vendor and generated paths —
    node_modules, .claude/, pnpm-lock.yaml, build outputs and caches — so a harness
    vendor update can never fail the format gate.
  • R12 (event): When pnpm build, pnpm test or pnpm dev has been run,
    git status --porcelain shall report no new untracked and no modified tracked files
    (every build output and cache is gitignored).

Acceptance criteria

  • A fresh clone + pnpm install + pnpm typecheck + pnpm test + pnpm build
    succeeds with no step outside the root package.json.
  • pnpm-lock.yaml is committed; package-lock.json is gone from the worktree and
    from the index.
  • .claude/hooks/lib/lemony.sh emit … still works after the install (the launcher
    finds the local bin).
  • git status --porcelain is empty after a full build + test + a dev session.

B. Front end — «Hola Mundo» proving the TanStack Start → Tailwind → shadcn chain (US4)

  • R13 (ubiquitous): apps/web shall be a TanStack Start application in TypeScript
    strict mode, with Tailwind configured and shadcn initialised with style new-york
    and base colour neutral.
  • R14 (event): When pnpm dev is run, the system shall serve apps/web on a local
    port and its root route shall render the «Hola Mundo» page.
  • R15 (ubiquitous): The «Hola Mundo» route shall render its content inside a shadcn
    Card and shall include a shadcn Button, so the styling chain is demonstrably
    applied end to end.
  • R16 (ubiquitous): The page shall use the shadcn/Tailwind default tokens only — no
    theme switcher, no dark-mode toggle, no custom palette (deferred by the PRD).
  • R17 (event): When pnpm build is run, the system shall produce a production build
    of apps/web and exit zero.
  • R18 (ubiquitous): The repository shall contain no file, route, asset, script or
    dependency left behind by a scaffolding CLI that is not used by the «Hola Mundo»
    page, the tests, the gates or the build.
  • R19 (event): When the front smoke test runs, it shall render the «Hola Mundo»
    route component and assert both the greeting text and the presence of the shadcn
    Button, without Docker, a live Mongo or network access.
  • R20 (ubiquitous): The front end shall perform no database access in this delivery,
    and apps/web shall declare no dependency on @memora/db.
  • R21 (unwanted): If the current shadcn release is incompatible with the Tailwind
    version TanStack Start installs, then the implementation shall pin the exact
    combination that works and state the pinned versions in progress.md and the PR
    description, instead of leaving a broken or half-working styling chain.

Acceptance criteria

  • pnpm dev serves a page showing the greeting inside a Card with a styled
    Button (Tailwind classes visibly applied).
  • pnpm test runs the smoke test green with Docker stopped and no network.
  • apps/web/package.json lists no unused dependency and no @memora/db.

C. MongoDB in Docker — reproducible local database (US2, US3)

  • R22 (ubiquitous): docker-compose.yml at the repository root shall define a
    single service built from image mongo:8, publishing port 27017, with no
    credentials, a named volume mounted at /data/db, and a healthcheck that reports
    the server as ready.
  • R23 (ubiquitous): docker-compose.yml shall carry a header comment stating it is
    for local development only — no credentials, never for an externally reachable
    environment.
  • R24 (event): When pnpm db:up is run, the system shall start the Mongo container
    detached and return only once the container reports healthy.
  • R25 (event): When pnpm db:down is run, the system shall stop and remove the
    container while preserving the named volume, so the data survives.
  • R26 (event): When pnpm db:reset is run, the system shall remove the container
    and its named volume and then start a fresh healthy container, so the database
    comes back empty.
  • R27 (unwanted): If the Docker daemon is not running, or port 27017 is already
    bound, then the db:* script shall surface Docker's own error message unchanged and
    exit non-zero — no masking, no retry loop, no silent success.
  • R28 (ubiquitous): The database name shall be memora and the canonical local URI
    mongodb://localhost:27017/memora.

Acceptance criteria

  • pnpm db:up → docker compose ps shows the service healthy.
  • Data written between db:up and db:down is still there after db:up again.
  • After pnpm db:reset the memora database is empty.
  • With Docker stopped, pnpm db:up exits non-zero and prints Docker's error.

D. packages/db — the only place that knows how to reach Mongo (technical decision)

  • R29 (ubiquitous): packages/db, published in the workspace as @memora/db, shall
    be the only location in the repository that resolves MONGODB_URI or constructs a
    Mongo client; no other workspace shall import the mongodb driver directly.
  • R30 (ubiquitous): @memora/db shall expose a pure resolver
    (resolveMongoUri(env)) that returns the configured URI for a given environment
    record, without reading global state itself.
  • R31 (unwanted): If MONGODB_URI is absent, empty or whitespace-only in the given
    environment, then the resolver shall throw an explicit error naming the variable and
    pointing at .env.example, and shall not fall back to any default URI.
  • R32 (ubiquitous): @memora/db shall expose a client factory
    (createMongoClient(uri)) that applies a server-selection timeout of at most
    5000 ms, so an unreachable server fails fast instead of hanging on the driver's
    default.
  • R33 (ubiquitous): @memora/db shall expose the two maintenance primitives the
    runner needs — a connectivity check (ping) and a database drop — operating on the
    database encoded in the URI, with no hard-coded database name.
  • R34 (ubiquitous): .env.example shall be versioned at the repository root with
    MONGODB_URI=mongodb://localhost:27017/memora, and .env shall be gitignored.
  • R35 (event): When an entry point starts, the system shall load the
    repository-root .env into the process environment if the file exists, and shall not
    fail when it is absent — the missing-variable failure of R31 is the only
    configuration failure path.
  • R36 (ubiquitous): @memora/db shall be consumed as TypeScript source (no build
    step, no emitted dist/) and shall typecheck on its own via pnpm typecheck.
  • R37 (ubiquitous): The driver shall be the official mongodb package with generic
    collection typing; no ODM (no Mongoose, no Prisma) shall be added.

Acceptance criteria

  • A unit test proves resolveMongoUri returns the value when present.
  • A unit test proves the absent/empty variable throws, and the message names
    MONGODB_URI.
  • grep for from 'mongodb' outside packages/db returns nothing.

E. Console runner — discoverable maintenance menu (US5, US7)

  • R38 (event): When pnpm runner is run, the system shall display an interactive
    arrow-key menu offering exactly: check Mongo connection, clean database, load seed
    data, exit — requiring no command-line flags.
  • R39 (event): When an operation finishes, the system shall return to the menu, and
    when exit is chosen it shall close any open Mongo client and exit with code 0,
    leaving no hanging process.
  • R40 (event): When «check connection» is chosen, the system shall connect with the
    resolved URI, issue a ping, and report success naming the host and database it
    reached.
  • R41 (unwanted): If the connection or ping fails (server down, timeout), then the
    system shall report the failure with the driver's message plus a hint to run
    pnpm db:up, return to the menu, and not crash with an unhandled-exception stack
    trace.
  • R42 (unwanted): If MONGODB_URI cannot be resolved when an operation needs it,
    then the runner shall print that explicit configuration error (R31) and return to the
    menu without attempting a connection.
  • R43 (event): When «load seed data» is chosen, the system shall print a message
    marking the operation as pending (TODO), perform no database access, and return
    a pending result marker — it shall never report work it did not do.
  • R44 (unwanted): If the user interrupts a prompt (Ctrl+C), then the runner shall
    terminate quietly — no unhandled-exception stack trace — closing any open client.
  • R45 (ubiquitous): Each runner operation shall be a function independent of the
    prompt layer, receiving its Mongo client/db as a parameter, so it can be tested
    against a double.
  • R46 (ubiquitous): The runner shall be executed with tsx directly over
    TypeScript, with no build step.

Acceptance criteria

  • pnpm runner shows the four-entry menu and navigates with arrow keys.
  • With Mongo down, «check connection» reports the failure and the menu is still
    usable.
  • «Load seed data» prints a TODO and the test proves no client method was called.
  • Unit tests cover every operation function against a Mongo double.

F. Clean database — the destructive operation (US6)

  • R47 (event): When «clean database» is chosen, the system shall first ask for an
    explicit confirmation that names the target database, before touching anything.
  • R48 (ubiquitous): The confirmation prompt shall default to no, so proceeding
    requires a deliberate keystroke.
  • R49 (state+event): While the confirmation has been accepted, the system shall call
    dropDatabase on the database resolved from the URI and report the outcome.
  • R50 (unwanted): If the confirmation is declined, then the system shall perform no
    write of any kind — dropDatabase shall not be called — and shall return a cancelled
    marker.
  • R51 (unwanted): If dropDatabase fails, then the system shall report the error
    and return to the menu, leaving the runner usable.

Acceptance criteria

  • A unit test proves accepted confirmation ⇒ dropDatabase called exactly once.
  • A unit test proves declined confirmation ⇒ no method called on the double, result
    marked cancelled.
  • Manual check: cleaning the real local database actually empties it.

G. CI and declared gates (US8)

  • R52 (event): When a pull request targeting main is opened or updated, GitHub
    Actions shall run a workflow that installs with pnpm (--frozen-lockfile, with the
    pnpm store cached) and then runs typecheck, format:check, test and build.
  • R53 (unwanted): If any of those four commands exits non-zero, then the workflow
    shall fail — no continue-on-error, no step allowed to swallow a red gate.
  • R54 (ubiquitous): The workflow shall take the Node version from .nvmrc and the
    pnpm version from the root packageManager field, so CI and local use the same
    versions.
  • R55 (ubiquitous): harness.config.yml shall declare gates: as exactly
    typecheck, format:check, test, build, and shall leave merge.allow_no_checks
    at its default false.
  • R56 (ubiquitous): The whole test suite shall be hermetic — no test shall require
    Docker, a live Mongo instance or network access.
  • R57 (ubiquitous): README.md shall document the quickstart for a fresh clone:
    install, db:up / db:down / db:reset, dev, runner, and the gate commands.

Acceptance criteria

  • The PR for this task shows a green GitHub Actions check with the four gate steps.
  • A deliberately broken type (locally) turns the typecheck step red.
  • harness.config.yml lists the four gates under gates:.

H. Definition of done — manual real-run verification

  • R58 (event): When the implementation is signalled done, progress.md shall record
    the outcome of the manual real-run sequence, executed in this order: pnpm db:up →
    pnpm runner → «check connection» reports success → pnpm dev → the «Hola Mundo»
    page loads in a browser → pnpm db:down.
  • R59 (ubiquitous): This manual sequence shall be treated as part of done; the
    hermetic suite does not cover it, and a green suite alone shall not be read as
    evidence that the infrastructure runs.

Acceptance criteria

  • progress.md contains the six-step verification with its result.

Design

See .claude/state/tasks/1/spec/design.md.

Tasks

See .claude/state/tasks/1/spec/tasks.md.

Activity

  1. brauliodiez commented on Oct 17, 2026

    @brauliodiez
    MemberAuthor

    Review — APPROVE

    Full pass, fresh context, on PR #2 at dcce210. Anchor: merge-base 95075e8 of the
    freshly fetched main and the PR head — 48 files added/modified, 1 deleted
    (package-lock.json), 42 changed non-test files.

    Verdict: APPROVE. No requirement fails. 58 of 59 PASS; R48 is PARTIAL on test
    coverage by a design decision the human approved at the spec gate. Six advisory /
    hardening findings, none blocking — two of them new to this pass.

    Evidence ledger: .claude/state/tasks/1/review-ledger/full-pass.json — validated green
    at this anchor (criteria 59/59, gates 4/4, passes 5/5, no spec-side problems).

    A pre-gate pass reviewed the same content while it was uncommitted and deliberately
    posted no record. I re-ran everything from scratch rather than inheriting it; where I
    reached the same conclusion I say so, and I note the two places I went further.


    Gates — re-run independently, cache bypassed

    The first turbo invocation can replay a cache, which is not fresh execution, so every
    script gate ran under TURBO_FORCE=true and the logs confirm
    cache bypass, force executing for each package.

    Gate Result
    typecheck ✅ 3/3 workspaces, fresh execution
    format:check ✅ "All matched files use Prettier code style!"
    test ✅ 20/20 (@memora/db 11, console-runner 7, @memora/web 2), no Docker daemon reachable
    build ✅ 1/1, exit 0, Generated .output/nitro.json
    pnpm audit ✅ no known vulnerabilities
    pnpm install --frozen-lockfile ✅ lockfile up to date, all 4 workspace projects in scope — CI's first step and its likeliest break

    The typecheck gate was independently proven to go red. There is no linter by
    explicit decision, so this gate carries the weight one would: I did not take it on trust.
    Mutating resolveMongoUri's return type string → number behind a restore trap produced
    rc=1, 0 successful, 3 total, Failed: console-runner#typecheck, error TS2322 in
    @memora/db and a cross-workspace TS2345 at console-runner/src/main.ts(51,34). Trap
    fired; tree clean.

    CI has now executed for the first time and reported green — new evidence no earlier
    pass could have had. gh pr checks 2 → Declared gates pass 31s; run
    39073679983 completed / success on pull_request. That closes R52–R54 empirically
    rather than by inspection.

    Real run — what a green suite cannot prove

    • Front end. pnpm dev, then GET / → 200, with lang="es", «Hola Mundo»,
      data-slot="card" / card-header / card-title and data-slot="button" («Empezar»)
      in the server-rendered HTML. Fetched the referenced /src/styles.css → 200,
      21 721 bytes, carrying the resolved utilities .bg-card,
      .text-card-foreground, .bg-primary, .text-primary-foreground, .rounded-xl,
      .min-h-screen, .shadow-sm, .max-w-md and the neutral token values
      (--card: oklch(1 0 0), --primary: oklch(0.205 0 0)). The styling chain is applied
      end to end, not merely imported. Dev log: zero errors, zero warnings.
    • No remote asset survived (R18). Zero external URLs in the served HTML; the only URL
      in the compiled CSS is Tailwind's own licence comment.
    • Runner, driven through a real pty (expect), four separate runs:
      • Exactly four menu entries with ↑↓ navigate • ⏎ select, and the arrow keys
        demonstrably moved the ❯ marker down the list.
      • Mongo unreachable → ✖ No se pudo conectar. plus the driver's own message verbatim
        (connect ECONNREFUSED ::1:27017, connect ECONNREFUSED 127.0.0.1:27017) plus
        ¿Está Mongo levantado? Prueba con \pnpm db:up`.`; menu returned, no stack trace.
      • Destructive confirm rendered Vas a BORRAR todos los datos de la base «memora». ¿Seguro? (y/N) — naming the database, defaulting to no — and Enter resolved to
        No → Operación cancelada: no se ha borrado nada.
      • Seed → the TODO, nothing touched.
      • MONGODB_URI unset with no .env load → the explicit configuration error and no
        ECONNREFUSED followed
        ; my script watched for one and would have failed the run.
        Menu stayed usable.
      • Ctrl+C → exit 0, and grepping the captured output for at …(,
        ExitPromptError and Error: gives zero hits (R44).
      • «Salir» → exit 0.
    • R12. git status --porcelain -uall empty after a full build + test + a dev
      session.
    • R5. .claude/hooks/lib/lemony.sh --version → 0.10.0;
      node_modules/.bin/lemony present, mode 755.

    Docker-dependent steps could not be executed — stated plainly. docker --version
    answers 27.3.1 in under a second, but docker info, docker compose ps and
    pnpm db:up each hang and had to be SIGKILLed after 10 s / 10 s / 25 s (no GNU
    timeout here; I bounded each call with my own watchdog). So R24–R27 rest on reading
    the scripts plus the Implementer's recorded runs
    , not on my own execution. What the
    reading does establish: all three db:* scripts are bare docker compose invocations
    with no wrapper, no retry loop, no || true and no redirection, so Docker's stderr and
    exit code propagate unchanged by construction (R27), db:down has no -v so the volume
    survives by construction (R25), and db:reset is down -v && up -d --wait so a failed
    teardown stops the sequence rather than racing a half-reset (R26).

    The rendered pixels are still not confirmed by an agent. No browser automation is
    available to me, so my front-end evidence is HTTP-level. The human performed the visual
    check at the checkpoint with the dev server on http://localhost:3000/; that is where
    that evidence lives.

    Spec compliance — 59 requirements, walked point by point

    Covered: 58/59 · Partial: 1 · Failing: 0 · Verdict: compliant.

    Per-requirement implementation + test evidence is in the ledger's criteria array, one
    entry per R<n>. The only non-PASS row:

    Req Summary Status Implementation Test
    R48 The destructive confirmation shall default to no ⚠️ PARTIAL console-runner/src/main.ts:116 — correct, and confirmed live — (prompt layer untested by approved design decision)

    No unspecified additions and no design divergence beyond what design.md's own decision
    log already records and the human signed (the separate vitest.config.ts, dropping
    exactOptionalPropertyTypes, hand-authoring apps/web instead of shipping CLI output,
    the two extra .prettierignore entries). On that last one I reached the same judgement
    as the earlier pass independently: harness.config.schema.json and
    docs/playbooks/README.md both exist, are installer-dropped artifacts regenerated by
    lemony repair/update, and so fall inside R11's general clause and squarely inside
    its stated rationale — "a harness vendor update can never fail the format gate". Compliant,
    not drift.

    Mutation probes — hand path, declared risk data-loss

    Seven files probed, thirteen mutants, each behind an EXIT restore trap. Tree
    verified clean after every probe; no .bak stranded.

    Mutant Outcome
    clean-database.ts — if (!confirmed) guard removed killed
    clean-database.ts — guard inverted to if (confirmed) killed (3/3 red)
    main.ts:116 — destructive confirm default: false → true SURVIVED
    main.ts:119 — cleanDatabase(target, confirmed) → (target, true) SURVIVED
    check-connection.ts — pnpm db:up hint dropped killed
    seed-data.ts — message claims success instead of TODO killed
    client.ts — timeout 5 000 → 50 000 ms killed
    client.ts — await db.command({ping:1}) deleted killed
    client.ts — await db.dropDatabase() deleted killed
    config.ts — ?.trim() dropped killed
    config.ts — return type string → number (typecheck probe) killed
    home-page.tsx — greeting → "Hello World" killed
    home-page.tsx — shadcn <Button> → bare <button> killed

    Eleven of thirteen killed. Both survivors land in console-runner/src/main.ts — the
    one logic-bearing file with no spec. The R50 guard that makes a declined confirmation
    touch nothing is genuinely protected; the prompt layer above it is not.

    Worth knowing at the checkpoint: this project declares no test:mutation script, so
    these probes were artisanal — one shell round trip each. Declaring one in the root
    package.json (driving Stryker or any mutation tool) would activate the cheap batched
    path on the next review.


    Findings

    1 · Advisory, headline — the destructive confirm is untested, and it is worse than one mutant shows

    console-runner/src/main.ts:116 reads default: false, // Enter cancels (R48). and that
    is correct — I watched the (y/N) prompt render and Enter cancel, with nothing
    dropped. But nothing automated holds it there, and I found a second, worse survivor
    the earlier pass did not:

    • main.ts:116 default: false → default: true — Enter stops meaning cancel and
      starts meaning delete the database. Suite stays 7/7 green.
    • main.ts:119 cleanDatabase(target, confirmed) → cleanDatabase(target, true) — the
      confirmation becomes pure theatre: the human types n, and the database is dropped
      anyway. Suite stays 7/7 green.

    This is the only destructive operation in the delivery, in a group that declared
    [risk: data-loss], and its entire protection is a one-off manual observation that does
    not regress.

    I did not reject on it, deliberately. The implementation meets R48, and the gap sits
    inside design.md's human-approved decision that the prompt layer is not tested
    ("testing it would mean driving a TTY"). Rejecting would overrule a decision the human
    signed at the spec gate. Cheapest closure if they want it closed, no TTY needed:
    extract a buildCleanConfirm(database) factory and assert default === false and that
    the message names the database (~10 lines); a second ~10 lines extracting the choice
    handler would cover the dispatch and close finding 5 too.

    2 · Security, medium / hardening — the credential-less Mongo publishes on all interfaces

    docker-compose.yml:20 is - '27017:27017', which binds 0.0.0.0. On any shared or
    untrusted network, every host on the LAN gets unauthenticated full read/write on every
    database — including the dropDatabase this delivery ships. The header comment warns
    against copying the file to a reachable environment, but the default binding already
    makes the dev machine reachable
    . '127.0.0.1:27017:27017' closes it and still satisfies
    R22's port publication and R28's mongodb://localhost:27017/memora. R22's letter is met,
    so this is a recommendation, not a block.

    3 · Security, low–medium / hardening — CI's token is write-scoped and its actions float (NEW)

    .github/workflows/ci.yml declares no permissions: block, and this repository's default
    is default_workflow_permissions: "write" with can_approve_pull_request_reviews: true —
    so the job's GITHUB_TOKEN can write to the repository. Its three actions are pinned
    to mutable major tags (actions/checkout@v7, pnpm/action-setup@v6,
    actions/setup-node@v7), so a compromised release of any of them would run with that
    write-scoped token on every same-repo PR, which is exactly the harness flow. One line
    fixes it:

    permissions:
      contents: read

    R52–R54 say nothing about token scope, so this is not a spec violation — a recommendation.

    4 · Advisory — Ctrl+D at a prompt exits 13, and it is undocumented

    Reproduced independently: stdin EOF at the menu leaves
    Warning: Detected unsettled top-level await at console-runner/src/main.ts:146 and
    ELIFECYCLE Command failed with exit code 13. R44 specifies Ctrl+C, which works
    correctly (exit 0, no stack trace), so this is unspecified rather than failing, and it is
    non-destructive — acceptable to ship for a local maintenance tool. But it is absent from
    progress.md
    , which R58/R59 make the durable real-run record. Recorded here so it is no
    longer undocumented.

    5 · Low / hardening and informational

    • main.ts:71 — the menu dispatch is an if-chain with no terminal else, so an
      unrecognised choice would spin the for(;;) loop silently instead of failing loudly.
      Unreachable today (closed typed union); hardening only.
    • apps/web/components.json:15 — the shadcn aliases utils (#/lib/utils) and
      hooks (#/hooks) point at paths that do not exist: apps/web/src/lib and
      apps/web/src/hooks are both absent, and the package's #/* map resolves to ./src/*.
      The next shadcn add will generate an import that does not resolve. Since this file's
      only purpose is future generation, the stale aliases are its whole failure mode.
    • toEqual instead of toStrictEqual in seven places (client.spec.ts:39/51/73,
      clean-database.spec.ts:23/31/44, check-connection.spec.ts:17), so an unexpected
      undefined-valued property would pass.
    • cn@0.5.0 — the shadcn components take cn from a third-party npm package rather
      than a local src/lib/utils.ts. Short package name (typosquat shape), carries a caret,
      and its lockfile entry declares hasBin: true. Mitigations are in place: pnpm 10 blocks
      install scripts and pnpm-workspace.yaml re-enables them for only esbuild and
      lightningcss, so cn runs no postinstall, and the lockfile pins an integrity hash.
      Worth pinning exactly, as the rest of the toolchain is.
    • progress.md's "pinned exactly (no carets)" claim is inaccurate for two
      apps/web dependencies — cn: ^0.5.0 and radix-ui: ^1.8.1. Everything in the
      tabulated list is indeed caret-free.
    • home-page.spec.tsx:10 — expect(getByRole(...)).toBeDefined() is redundant
      (getByRole already throws). Cosmetic: I probed the test and both the greeting and the
      shadcn Button marker are genuinely load-bearing.

    Checked and clean — recorded so the absence is evidence, not silence

    • R29, the single door to Mongo. Grepped the whole tree: the only driver import is
      packages/db/src/client.ts:1, and packages/db/package.json is the only manifest
      declaring mongodb. apps/web declares no @memora/db (D1).
    • No secret leaks. The configuration error quotes only the example-URI constant, never
      the resolved value, so a MONGODB_URI carrying credentials is not echoed. .env is
      untracked (git check-ignore -v .env → .gitignore:17); .env.example holds a
      placeholder only.
    • No unused dependency hiding in packages/db. I probed the plausible one: removing
      types: ["node"] from packages/db/tsconfig.json makes tsc fail (TS2591 on
      process.env in the spec, TS2740 on MongoClientOptions), so it earns its place.
      Every apps/web dependency traces to a real import, the runtime or a gate.
    • closeClient's empty catch is compliant, not a silent failure: it carries an explicit
      comment stating the intent, which is the rule. Every other error path is loud, with the
      underlying message kept verbatim.
    • No shell or path injection surface. The db:* scripts and the runner script are
      static strings; nothing in the change builds a command or a path from input.
    • docs/architecture.md is absent, so the architecture-shape check does not apply —
      and nothing here suggests creating one.

    Reviewed-tree: c194b832753a90de30d66b217adefd04fae7ab33
    Diff-fingerprint: 1192498ee9e6180a0f845c3f8387daca1807484b

  2. added and removed
    harness:status:in-reviewManaged task: implemented, awaiting review/merge.
    on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    harness:managedIssue managed by the Lemony harness.harness:sddFollowed the full Spec-Driven Development flow.harness:status:doneManaged task: merged and closed.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions