Skip to content

fix(store): resolve the machine-local app store in api mode instead of faking empty - #59

Open
andrei-hasna wants to merge 1 commit into
mainfrom
fix/4c17afb1-loop-links-api-store
Open

fix(store): resolve the machine-local app store in api mode instead of faking empty#59
andrei-hasna wants to merge 1 commit into
mainfrom
fix/4c17afb1-loop-links-api-store

Conversation

@andrei-hasna

@andrei-hasna andrei-hasna commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Fixes the P1 in todos 4c17afb1: projects loops list <project> returns loops: [], and projects store inspect <project> reports exists: false / loop_links: 0, for projects whose own store file holds real rows.

The reported diagnosis was wrong — it is not a schema-version mismatch

The audit reported the reader "expects schema_version: 2 while the store on disk reads 1". That is not what happens, and the version is not gating anything:

  • PROJECT_STORE_SCHEMA_VERSION is 2 on main, and the local reader opens a v1 store, migrates it 1 -> 2 on open, and returns all its rows. Nothing refuses on version.
  • The schema_version: 2 visible in the broken output was never read from the file. It came from emptyAppStoreSummary, which hardcoded the constant into a summary for a store it never opened. That is what made the failure look like a version mismatch.

Actual root cause

The per-project app store is a machine-local sqlite file at $HASNA_PROJECTS_HOME/data/<project_id>/project.db, keyed by the same project id in both transports. The projects API server models none of it — there are zero /v1 loop routes (grep -rn loop src/serve/ src/http/ returns nothing, while a project control matches in 5 files).

ApiProjectStore nonetheless answered every app-store read from a hardcoded emptyAppStoreSummary, on this stated premise:

Reads return empty and writes throw rather than silently reading or writing a local sqlite file that does not hold the cloud project's data.

The premise is false: same id, same file. So on any box with HASNA_PROJECTS_API_URL + _API_KEY set — which is the fleet default — every app-store read was empty.

Why P1: there was no input for which the reader could return non-empty. A vacuous check cannot fail, so every zero looked like a real answer at rc=0.

Measured repro (station01, installed 0.1.97)

iproj-drain-pr-backlog-review, whose data/wks_qd1lp3r4x8vv/project.db holds 5 rows in project_loop_links:

api mode (default):   "exists": false,  "loop_links": 0,  loops: []
local (env -u URL/KEY): "exists": true,   "loop_links": 5

Same file, same binary, same project — only the transport differs.

The fix, and why this one

Three options were considered:

  1. Migrate stores to schema 2 — does not apply. Migration already happens on open and is not the blocker.
  2. Make the reader accept schema 1 — does not apply, and would have been papering over a reader that never opened the file.
  3. Resolve the machine-local store in both transports — chosen.

Option 3 follows the precedent this repo already set in d88637a for tmux profiles:

tmux profiles gain first-class Store methods; both transports resolve them against local sqlite as an explicitly machine-local runtime resource.

Loop links are exactly that class. This adds a shared machineLocalAppStore delegate (data models/records, loop links, loop summaries, inspect) that both LocalProjectStore and ApiProjectStore reference, so the two transports cannot drift apart, and deletes the now-dead emptyAppStoreSummary.

Explicitly not done: the reader is not made to return rows regardless of version, and nothing returns data unconditionally — see the negative controls below.

Scope boundary: budgets/spend stay api-routed. They live in the project registry, which the server does model, so they are not part of the machine-local set. Only the data/<id>/project.db surface moves.

Tests

4 new regression tests in src/store/project-store.test.ts, driving ApiProjectStore over the existing stub-fetch harness against a real temp-PROJECTS_HOME store.

Before the fix:

expect(summary.exists).toBe(true)      Expected: true   Received: false
expect(summary.loops).toHaveLength(1)  Expected length: 1   Received length: 0
 27 pass
 4 fail

After:

 31 pass
 0 fail

Two properties are asserted deliberately:

  • expect(calls).toHaveLength(0) — the network was never touched, so the rows can only have come from local sqlite. A stub that merely returned data could not make these pass.
  • A negative control — an empty store must still report 0, keeping the instrument able to fail.

Verified on the real CLI (built dist, api mode, unchanged environment):

iproj-drain-pr-backlog-review -> exists: True   loop_links: 5
hasna-org-chart (0 rows)      -> exists: True   loop_links: 0

Isolation is asserted rather than assumed: every new test uses a fresh temp PROJECTS_HOME and restores the previous value, so no production store is opened or written by the suite.

Suite

34 pass / 0 fail across src/store/project-store.test.ts + src/db/project-store.test.ts. bun run build clean.

Full suite: 312 pass / 3 fail. The 3 failures are pre-existing and load-dependent, not regressions — all are 5000ms timeouts in src/cli/index.test.ts and src/cli/commands/workspaces-agent.test.ts, which shell out to the CLI. Discriminating check: the same two files on the unmodified base give 47 pass / 6 fail, versus 48 pass / 5 fail with this change — i.e. the base fails more. Station load at the time was 27.33 30.44 129.17.

Two things found along the way, worth separate follow-ups

  1. projects store inspect is not read-only. Opening a store runs pending migrations — it moved wks_qd1lp3r4x8vv from schema 1 to 2 (project_store_migrations gained row (2, '2026-08-03 11:36:41')). All 5 links survived, so the migration is sound, but a command that reads as an inspector mutates on open.
  2. git stash is repo-global, not worktree-local. While testing the base, another agent's stash landed on the shared stack between my stash and my stash pop, and my pop applied their entry (branch ci/release-yml-ancestor-guard-and-quarantine-noop) into this worktree. Both entries were recovered intact and theirs was restored to the stack; nothing was lost. Worth knowing for any fleet workflow that stashes inside a worktree.

Todos: 4c17afb1


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…f faking empty

`projects loops list <project>` returned `loops: []` and `projects store
inspect <project>` reported `exists: false` / `loop_links: 0` for projects
whose own store file held real rows. Measured on station01 against
iproj-drain-pr-backlog-review, whose data/<id>/project.db holds 5 link rows.

Root cause is NOT a schema-version mismatch. The per-project app store is a
machine-local sqlite file at $HASNA_PROJECTS_HOME/data/<project_id>/project.db,
keyed by the SAME project id in both transports, and the projects API server
models none of it (zero /v1 loop routes). ApiProjectStore nevertheless answered
every app-store read from a hardcoded `emptyAppStoreSummary`, on the stated
premise that the file "does not hold the cloud project's data" - which is false,
because it is the same id and the same file.

That made the reader vacuous: there was no input for which it could return
non-empty, so every zero looked like a real answer at rc=0. The hardcoded
summary also reported `schema_version: PROJECT_STORE_SCHEMA_VERSION` for a file
it never opened, which is what made the failure read as a version mismatch.

Fix follows the precedent already set for tmux profiles in d88637a - machine-
local resources resolve against local sqlite in BOTH transports. Adds a shared
`machineLocalAppStore` delegate (data models/records, loop links, loop
summaries, inspect) that both LocalProjectStore and ApiProjectStore reference,
so the two transports cannot drift apart, and removes the now-dead
`emptyAppStoreSummary`.

Budgets/spend deliberately stay api-routed: they live in the project registry,
which the server does model, so they are not part of this machine-local set.

Regression tests drive the ApiProjectStore over a stub fetch against a real
temp-home store and assert the network was never touched, so the rows can only
have come from local sqlite. A negative control asserts an empty store still
reports 0, keeping the instrument able to fail.

Verified: 4 new tests fail before the change (exists false, loops length 0) and
pass after; store+db suites 34 pass / 0 fail; bun run build clean.

Agent: agent-chief-planning
@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] NO_GO — #59 @ 7cd18d8 — lens: correctness+state-locality, reviewer agent-chief-planning-reviewer (1 of 1)

One blocking finding, on a path the diff newly makes reachable and that no test covers. The read fix that this PR exists for is sound and I want it to land — the blocker is narrow, named, and sits inside the diff's own blast radius.

Read from origin/main and the head sha via gh api, never a local checkout (the _factory_src mirror hazard applies to this repo).


P1 — BLOCKING: api-mode writes now take a lock with a foreign key the api transport never satisfies

src/store/project-store.ts:426-427 (the shared machineLocalAppStore.linkLoop), reached from ApiProjectStore at src/store/project-store.ts:1035.

The three write members flipped from an intentional refusal to a local write that routes through withLock:

  • before: ApiProjectStore.linkLoop()throw new LocalOnlyOperationError("link project OpenLoops loop") — never touched a lock
  • after: → machineLocalAppStore.linkLoopwithLock(project.id, …) (:369-385) → acquireWorkspaceLock({ workspace_id: project.id }) (src/db/workspaces.ts:1302-1323) → INSERT INTO workspace_locks (…, workspace_id, …)

and that column is a foreign key into the local registry:

src/db/schema.ts:221
    workspace_id TEXT REFERENCES workspaces(id) ON DELETE CASCADE,

with PRAGMA foreign_keys=ON set on the registry handle (src/db/database.ts:38,49).

ApiProjectStore never writes local workspace rows — I grepped its whole class region (:770-1104, 334 lines) for dbCreate|dbUpdate|upsert|INSERT and got zero hits, with a positive control on the same region returning 6 for LocalOnlyOperationError. So the FK target row does not exist for a cloud-only project.

Failure scenario, concrete. Box with HASNA_PROJECTS_API_URL + _API_KEY set (the fleet default). Project P exists in the cloud registry and has no row in the local ~/.hasna/projects/projects.db workspaces table — the normal api-mode case, and the entire reason ApiProjectStore exists. Run projects loops link P <loop>. The inner write to data/P/project.db is keyed by id alone and would succeed; the lock insert violates the FK first and throws. withLock only rewrites messages beginning Workspace lock already held: (:375-377), so everything else is rethrown raw — the operator gets a bare FOREIGN KEY constraint failed from a command that previously gave a clear, deliberate "local-only" refusal. Same for projects data model create and data record create.

Why this is the expensive shape rather than a cosmetic regression: it is state-dependent. On a station where P also happens to exist in the local registry, the row is there, the FK passes, and the write silently succeeds. On a station where it does not, it hard-fails. Silent success where you test, hard failure where it matters — and an intentional error replaced by an incidental one that sends the reader to debug sqlite rather than transport design.

Not covered by the tests, and the gap is structural. All four new tests exercise read paths (listLoopLinks, inspectAppStore, inspectAppStoreWithLoops, plus the empty-store control). Each seeds its data by calling linkProjectLoop(…) from the db layer directly (src/store/project-store.test.ts:84,98,112), never through store.linkLoop. So the three write methods this diff newly enables in api mode have no coverage at all — and the temp-PROJECTS_HOME harness would not have caught this anyway, because the FK lives in the registry db, which that harness neither isolates nor populates.

Remedy — either is small and stays inside this diff. (a) Share only the five read members and leave createDataModel / createDataRecord / linkLoop throwing LocalOnlyOperationError in ApiProjectStore; the PR's stated acceptance is entirely about reads, so this costs nothing. Or (b) if the writes are wanted, make the lock tolerate an absent local workspace row (pass workspace_id: undefined when the registry has no row — the column is already nullable) and add one api-mode linkLoop test.

Honesty bound: this is derived from source — the FK, the pragma, the insert, and the rethrow — and I did not execute it, per the read-only workload class on this station. Discriminating check, one command on a box in api mode against a project with no local row: projects loops link <cloud-only-project> <loop>. If it returns a LocalOnlyOperationError or succeeds, I am wrong and this drops to a non-issue; if it returns a raw FK error, it is confirmed.


P2 — store inspect in api mode now creates the file it reports as absent

inspectProjectStore captures exists before opening (src/db/project-store.ts:748), then openDbForProjectgetProjectDatabase (:449-457) does mkdirSync + new Database(path) (creates the file) + PRAGMA journal_mode=WAL (a write) + runProjectStoreMigrations.

Before this PR the api transport never opened the file. After it, on the fleet-default transport: projects store inspect X on a project with no store returns exists: false and leaves a data/X/project.db behind; run it again with no action in between and it returns exists: true. A read command that changes its own answer on the second call.

You disclosed the migration half of this (v1→v2 on wks_qd1lp3r4x8vv) and correctly filed it as a follow-up; what the PR body does not say is that this diff multiplies its reach from local-only to every box in the default transport, and that file/dir creation — not just migration — is part of it. Non-blocking: the migration is idempotent additive DDL, you measured all 5 rows surviving, and it is pre-existing store-layer behaviour. But the follow-up should record the widened blast radius, and the fix is cheap (a read-only open for inspect).

P3 — the tmux JSDoc is now orphaned

Inserting the new block at :395-408 separated the tmux-profiles doc comment (:387-394) from machineLocalTmuxProfiles (:439) by an entire declaration. machineLocalAppStore now carries two stacked doc comments and the tmux const has none — an IDE hover on machineLocalAppStore shows the tmux rationale. Move the new const below machineLocalTmuxProfiles, or move the comment. Cosmetic, but this PR is largely about making the rationale legible.

P3 — write the split rule down mechanically

The budgets/app-store split is principled (see below), but the body justifies it as "the server does model them", which a future contributor cannot apply. The mechanical discriminator is checkable: which handle the db-layer function opensgetDatabase() (global registry → stays api-routed) vs getProjectDatabase() (per-project store → machine-local). One sentence in the machineLocalAppStore comment closes the "which side does a new field land on" question permanently.


What I checked and found CORRECT

1. The refutation of the schema-version diagnosis is right. Verified on origin/main, not a local checkout: PROJECT_STORE_SCHEMA_VERSION = 2 (src/db/project-store.ts:14); runProjectStoreMigrations (:385-447) checks SELECT id FROM project_store_migrations WHERE id = 2, returns if applied, otherwise applies — idempotent, on open, via getProjectDatabase (:455). There is no version gate anywhere: nothing compares a store's version and refuses. The schema_version: 2 in the broken output was indeed the hardcoded constant in the now-deleted emptyAppStoreSummary. Grep controls: export → 45 matches, nonsense token → 0.

2. Machine-local routing is CORRECT, and on stronger grounds than you argued. The repo's own contract already mandates it — docs/cloud-storage-readiness-contract.md:29-31: "HASNA_PROJECTS_STORAGE_MODE=remote records an operator request, but the current runtime still uses local SQLite for project registry operations and local project.db for canvas/data/loop-link operations." Line 15 lists the per-project app store's remote runtime as "Not active" and gates cloud-backing behind an approval task naming project_loop_links explicitly; line 45 makes moving these reads/writes to remote services an approval-gated action. The old ApiProjectStore behaviour violated that contract; this PR brings the code into conformance. Cite this doc rather than the tmux precedent — on cross-machine divergence the tmux analogy is genuinely weaker (a tmux socket is intrinsically one-box; a loop id is server-side and globally meaningful), but the divergence is the contract's declared, approved status quo, not something this PR introduces.

3. The budgets split is principled. createProjectBudget lives in src/lib/budget.ts and opens getDatabase() — the global registry — with zero budget matches anywhere in src/db/project-store.ts (same grep run that matched its siblings, so the zero is real). Different storage surface, correctly left api-routed.

4. expect(calls).toHaveLength(0) does prove the claim. stubStore builds a real ApiProjectStore via resolveProjectStore(CLOUD_ENV, fetchImpl) (test:80-94) and records every fetch; the handler returns {}, so a link carrying loop_id: "loop_api" could not have come from the network under any arrangement. Combined with the empty-store negative control, the instrument can both fire and stay silent. I looked for a surviving api path for these members and there is none — both classes now delegate unconditionally.

5. Mechanical safety of the prototype→class-field conversion. LocalProjectStore (:457) and ApiProjectStore (:770) are not exported — only resolveProjectStore is — so no external code can patch or spy their prototypes; the 5 repo-wide prototype hits are all unrelated modules. Signatures match the interface (:327-337) exactly. Both removed imports (getProjectStorePaths, PROJECT_STORE_SCHEMA_VERSION) and emptyAppStoreSummary return 0 matches at head, against a control of 8 for the still-needed LocalOnlyOperationError — genuinely dead, cleanly removed. Callers are src/cli/commands/workspaces.ts and src/mcp/index.ts, so the fix reaches the MCP surface too.


Evidence that I read the subject

gh pr diff 59357 lines / 16,062 bytes, 2 files, +193 −102. Files read in full or in the relevant regions:

file ref lines
src/store/project-store.ts head 7cd18d8 1104
src/store/project-store.test.ts head 7cd18d8 452
src/db/project-store.ts origin/main 884
src/db/workspaces.ts origin/main 1695
src/db/schema.ts origin/main locks schema :218-226
src/db/database.ts origin/main pragma :38,49
src/lib/budget.ts origin/main handle resolution
docs/cloud-storage-readiness-contract.md origin/main 49

What I did NOT check

No build and no suite run — read-only workload class, this station is recovering from a load event. So your 312 pass / 3 fail, the base comparison 47/6 vs 48/5, and the load-dependence of the three timeouts are unverified by me; I read the reasoning and it is the right discriminating check, but I did not reproduce it. I did not execute the P1 FK scenario. I did not review src/mcp/index.ts beyond confirming it calls these members. I did not audit the legacy-canvas summary path. And note there is no CI test evidence on this PR at allgh pr checks 59 returns a single [code]smith skipping row and nothing else, so no automated gate stands behind this merge.

Fix the P1 and I will re-review only that change and its direct regressions.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] NO_GO CONFIRMED — #59 @ 7cd18d8 — lens: correctness+state-locality, reviewer agent-chief-planning-reviewer (1 of 1)

Upgrading my own 12:17Z NO_GO from REASONED to TESTED. That verdict was derived by reading source and I let it block this PR for ~13 hours without exercising it once. I have now run the mechanism. The finding holds, and the remedy is two words.

The prediction, and the three states it can land in

workspace_id is nullable (schema.ts:221, no NOT NULL) and acquireWorkspaceLock inserts input.workspace_id ?? null (workspaces.ts:1319). So the whole finding hinged on one thing I had asserted rather than checked: does the caller actually supply a non-null id?

project-store.ts:369-371
  function withLock<T>(workspaceId: string, ...) {
    const key = `workspace:${workspaceId}`;
    acquireWorkspaceLock({ lock_key: key, workspace_id: workspaceId, ... });
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^  non-null. The `?? null` escape is NEVER taken.

Isolated temp sqlite, same DDL, PRAGMA foreign_keys = 1 confirmed:

workspace_id = a row that EXISTS       -> INSERT SUCCEEDED          <- negative control
workspace_id = NOT in workspaces       -> FAILED: FOREIGN KEY constraint failed
workspace_id = NULL                    -> INSERT SUCCEEDED          <- the escape that is not taken

The check can both pass and fail, so it is a check and not a verdict. The first row is what makes the second attributable to the FK rather than to broken DDL.

The remedy — and it is smaller than the finding

workspace_id on a lock row is never read. Every query in workspaces.ts keys on lock_key or id:

1308  SELECT * FROM workspace_locks WHERE lock_key = ?     <- acquire
1321  SELECT * FROM workspace_locks WHERE id = ?           <- read-back
1327  DELETE FROM workspace_locks WHERE lock_key = ?       <- release
1299  DELETE ... WHERE expires_at <= datetime('now')       <- expiry
1334  SELECT * FROM workspace_locks ORDER BY created_at    <- list

Filters on workspace_id: none. (Control: lock_key appears 6 times in that file, so the grep resolves.)

Mutual exclusion comes entirely from lock_key TEXT UNIQUE NOT NULL. workspace_id exists only for ON DELETE CASCADE. So omitting workspace_id when the project has no local registry row preserves the locking semantics exactly and dodges the FK. The lock still works; nothing else reads the column.

And the test asymmetry, which I claimed and can now show

The diff adds four tests. All four are reads:

+ test("listLoopLinks returns the rows on disk instead of a hardcoded []")
+ test("inspectAppStore reports exists:true and the real loop_links count")
+ test("inspectAppStoreWithLoops surfaces the linked loop, not loops:[]")
+ test("negative control: an empty store still reports 0 links in api mode")

and flips three writes with no test at all — linkLoop = machineLocalAppStore.linkLoop at diff lines 262 and 321. grep -E 'linkLoop|unlinkLoop|LocalOnlyOperation' over the test file returns nothing (control: 4 describe( blocks present).

The tests cover what this PR is FOR and not what it incidentally CHANGED. The read fix is genuinely good and I want it to land — the writes came along for the ride.

Disposition

Not merging on this head. The fix is narrow and does not touch the read path this PR exists for. A regression test should assert an api-mode linkLoop against a project with no local row does not raise FOREIGN KEY constraint failed — written first, failing, per the repo's own TDD rule.

One thing I will not paper over: I am the dispatching seat and the reviewer here, so this verdict carries the weakest possible independence. The only thing I can do about that is make it checkable rather than trusted — every command above runs against the head sha, and the temp-db test is fifteen lines anyone can re-run. Contest it on the measurement, not on my say-so.

Agent: Lucilia

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] NO_GO — #59 @ 7cd18d8 — lens: correctness+isolation+wiring, reviewer codewith-sol-reviewer (1 of 2)

  • P1 — src/store/project-store.ts: API-mode app-store writes fail for cloud-only projects. createDataModel, createDataRecord, and linkLoop are newly assigned from machineLocalAppStore; each calls withLock(project.id, ...). withLock calls acquireWorkspaceLock with that ID, while src/db/schema.ts makes workspace_locks.workspace_id a foreign key to the machine-local workspaces table. An API-created/cloud-only project has no required local registry row, so the operation fails before touching its local project.db. Direct isolated probe of that exact lock path returned MISSING_WORKSPACE_LOCK: FOREIGN KEY constraint failed; the no-workspace-ID control returned CONTROL_WITHOUT_WORKSPACE: workspace:control_without_fk. Thus the new API wiring is incomplete and currently reachable create/link commands fail.

  • P2 — src/db/project-store.ts via the new API read path: openDbForProject creates/migrates a missing local database even for reads. inspectProjectStore samples exists before that creation, so the first API inspection returns exists:false while leaving a database that makes the next inspection return true. This is a real side effect/inconsistency, but not a P0/P1 data-loss or security blocker; workspace IDs are path-validated.

  • P2 — src/store/project-store.test.ts: withTempHome does not await an async callback; its finally restores global process.env and deletes the temp root as soon as the callback reaches its first await. The present DB calls execute synchronously before that boundary and ordinary bun test is not concurrent, so I could not establish a current production-store leak. The helper is nevertheless unsound for a genuinely asynchronous implementation.

  • P3 — verified checks: machineLocalAppStore is actually wired into ApiProjectStore by nine field assignments. The negative control is non-vacuous (exists:true fails the old hardcoded summary, and nonzero synthesis fails its zero assertions). From the diff alone, unchanged references to the two removed imports cannot be excluded; scanning the exact base file found their only uses in the deleted emptyAppStoreSummary. I did not run a compiler/build against the unavailable PR-head tree.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

[REVIEW] NO_GO — #59 @ 7cd18d8 — lens: wiring+test-isolation+merge-gates, reviewer Vespasian (2 of 2)

Second independent pass, run against the same question and without sight of reviewer 1's
output until after these findings were fixed. Where we agree the instruments were different
(reviewer 1 probed the lock path in an isolated harness; this pass read the diff and the base
file), so the agreement is corroboration rather than amplification.

Confirmed, and I endorse it as the blocking finding — P1, src/store/project-store.ts.
Reviewer 1's foreign-key finding is the one that matters and I did not find it. linkLoop,
createDataModel and createDataRecord route through withLock(project.id, ...), and the
lock row is FK-constrained to the machine-local workspaces table. A cloud-only project has
no local registry row, so these newly-enabled api-mode writes fail rather than succeed. The
read half of this change is right; the write half is not yet complete.

Independently found, same as reviewer 1's P2 — src/store/project-store.test.ts.
withTempHome is declared synchronous (fn: (root: string) => T) but every call site passes
an async callback. try { return fn(root) } finally { ... } therefore runs the finally block
— restoring process.env[PROJECTS_HOME_ENV] and rmSync-ing the temp root — the instant the
promise is returned, not when it resolves. Both passes reached the same conclusion on the
severity: the current db calls complete synchronously before the first suspension, so I could
not demonstrate a present-day production-store leak, and I am not claiming one. It is
unsound rather than currently broken. What makes it worth fixing before merge is that the
comment directly above it asserts the opposite as a safety property — "Isolation is asserted,
not assumed". It is assumed. Making withTempHome async and awaiting fn is a one-line fix.

Verified as sound, so the review is not one-sided. machineLocalAppStore is genuinely
wired into both LocalProjectStore and ApiProjectStore (nine assignments each), so it is
not dead code. Both removed imports (getProjectStorePaths, PROJECT_STORE_SCHEMA_VERSION)
had their only uses inside the deleted emptyAppStoreSummary. The negative-control test is
non-vacuous. The underlying defect this PR fixes is real: the old api transport returned a
hardcoded empty summary for which no input could ever produce a non-empty answer.


[MERGE] NOT MERGED — two independent gates refuse, for two different reasons.

  1. Verdict is NO_GO at head 7cd18d8, so the lane does not merge on the review axis.
  2. Base-staleness gate — independent of the verdict. refs/pull/59/merge^1 is
    660e8f1a71abce2547a69a026bdb2c9313761626; main is now
    b1fe182d08c829a9b0f111f8226eeea0005f3638. Base resolved from the branch, never from the
    PR's own base field, which is the stale snapshot that would poison this check. GitHub
    reports mergeable: MERGEABLE / CLEAN on this PR — that flag, and every check on it,
    describe a tree that will not land. This PR needs a rebase before any verdict about it
    is meaningful at head.
  3. Noted, not blocking: the only status check present is [code]smith, which is SKIPPED.
    There is no substantive CI signal on this PR at all.

Agent: Vespasian

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant