12 KiB
Open contradictions
Opened 2026-07-29 by phase 0, against commit
c99e3f4. Phase 0 only finds these. It resolves none of them.
Each row is claim A vs claim B with a location for both, and the phase that owns the resolution.
When a phase resolves one, it moves the row to Resolved at the bottom with the decision, and — per
the chain's non-negotiable #5 — records the decision in docs/status/decisions.md.
Three of the five contradictions the plan seeded turned out to be narrower than stated. Those are written up as they actually are, with the evidence, in § Corrections to the seeded list.
Open
| # | Claim A | Claim B | Owner | Status |
|---|---|---|---|---|
| C-1 | Set the crypto keys with dotnet user-secrets — manual-testing-plan.md:22, :233 |
user-secrets is not used; <UserSecretsId> was removed so the store is not read — CLAUDE.md:90, DEPLOY.md:20, server/CLAUDE.md:70, and Baya.Web.Api.csproj (no UserSecretsId element) |
3 | open |
| C-2 | Placeholder value is literally SET_VIA_USER_SECRETS_OR_ENV — server/src/API/Baya.Web.Api/appsettings.json (6 occurrences), enforced by StartupSecretsGuard and by the pre-commit hook |
That store does not exist any more (C-1's B side). The name instructs a reader to use a removed mechanism | 2 | open — naming only, behaviour is correct |
| C-3 | The server listens on https://localhost:5002 — CLAUDE.md:62, :135, api-conventions.md:6, RUNBOOK.md:7, :84, :112, + 12 more |
It listens on http://localhost:5002 — launchSettings.json:25 ("applicationUrl": "http://localhost:5002"), and client/.env.development:20 (NEXT_PUBLIC_API_URL = http://localhost:5002) |
2 + 3 | partly resolved by phase 1 — the three rule-file occurrences (root CLAUDE.md ×2, server/CLAUDE.md) now say http. api-conventions.md (phase 2) and RUNBOOK.md + the remaining 12 (phase 3) are untouched |
| C-4 | RUNBOOK's dotnet dev-certs https --trust step is required, because the browser would otherwise reject the API — RUNBOOK.md:26 |
The API is plain HTTP locally (C-3's B side), so there is no certificate to trust | 3 | open — likely a dead step |
| C-5 | Bring-up starts a local SQL Server in Docker on localhost:1433 — RUNBOOK.md:18, :35–48, :63 |
The committed dev config points at a remote SQL Server — appsettings.Development.json (Server=87.107.152.16,1433), and manual-testing-plan.md:20 calls the remote one "currently" the target |
3 | open — the two bring-up paths give different worlds: the remote DB is already seeded, a fresh local one is not |
| C-6 | GET /api/v1/webhooks/payouts/{provider} does not exist — absent from dev/contracts/openapi/swagger.v1.json (frozen 2026-07-13) and from every dev/contracts/domains/*.md |
It exists — present in the fresh snapshot docs/integration/openapi/swagger.v1.json (2026-07-29). See § OpenAPI drift |
2 | open |
| C-7 | GeneratePayoutBatchCommand's shape per the 07-13 snapshot |
A different shape in the 07-29 snapshot — the only schema of 339 whose definition changed | 2 | open |
| C-8 | Two contract files describe the same domain — dev/contracts/domains/messaging.md is a headerless 851-byte fragment ("Refinement phase 3 additions (REQ-028)") sitting beside the 10.8 K messaging-notifications-admin.md, which it silently amends |
— | 2 | open — merge, don't move both |
| C-9 | The OpenAPI folder publishes documents v1 and v1.1 — openapi/README.md:3 |
Only swagger.v1.json has ever been committed; v1.1 was not fetched during this survey |
2 | open — UNVERIFIED, check /swagger/v1.1/swagger.json when the server is next up |
| C-10 | 18 hardening items are open — issues.md, 18 of 18 checkboxes unticked, last touched 2026-07-17 | Fourteen UI phases, two manual-testing iterations, a Telegram integration and a deploy commit ran afterwards (12ce7fa → 96b57eb, 07-20 → 07-28) without ticking any box |
4 | open — Phase 4 must re-verify each item against code, not trust the checkbox |
| C-12 | The skill is current | It is exactly one iteration behind the code. SKILL.md's last commit is baa3cc6 ("manual improvement 1"); client/CLAUDE.md's is e6a8f93 ("manual improvement 2"), which changed 44 files, +3419/−2449 under client/src. Anything iteration 2 changed is absent from the skill |
7 | partly resolved by phase 1 — the design-language half is corrected (see R-2); phase 7 still owns the skill's own workflow/procedure content |
| C-13 | dev/ is "the plan for building Balinyaar", written in the imperative — dev/README.md:3 |
It is a record of work already done. dev/phases/ last touched 2026-06-28; the code it describes shipped weeks ago |
6 | open — resolved by the archive banner, not by editing 199 files |
| C-14 | temp/swagger.json is a stale committed duplicate — _plan/README.md diagnosis table |
It is not committed at all — .gitignore:1 ignores temp, and git ls-files temp/ is empty. It is local clutter, not repo content |
0 | see § Corrections |
OpenAPI drift
The fresh snapshot is at docs/integration/openapi/swagger.v1.json.
See its README for provenance. Measured against the 2026-07-13 snapshot
at dev/contracts/openapi/swagger.v1.json:
| 2026-07-13 | 2026-07-29 | |
|---|---|---|
| paths | 177 | 178 |
| operations | 185 | 186 |
| component schemas | 339 | 339 |
Added (1)
POST /api/v1/webhooks/payouts/{provider}
Removed (0) · Methods changed on an existing path (0) · Schemas added/removed (0)
Schema definitions changed (1)
GeneratePayoutBatchCommand
This is Phase 2's worklist for the machine contract, and it is two lines long. The 16-day gap between the snapshot and the code produced almost no wire-level drift. Phase 2 should therefore spend its budget on the hand-written
dev/contracts/domains/*.md— 17 files, frozen the same day, never mechanically checked against anything — rather than on the JSON. See § Corrections C-14b.
Corrections to the seeded list
The plan seeded five contradictions. Two hold as written; three are narrower than stated. Recording this here so later phases do not budget for work that is already done.
Holds — user-secrets. The plan says 18 files still instruct you to use it. 32 mentions across 18
files is right, but the split matters: 6 files document the removal correctly (root CLAUDE.md,
DEPLOY.md, server/CLAUDE.md, server/CONVENTIONS.md, and RUNBOOK.md twice — including a
troubleshooting row that names the error you now get). 12 are historical — refinement prompts,
handoffs and reports written before the removal, all headed for archive/. Only one live document
still gives the wrong instruction: manual-testing-plan.md
(last commit bd06ef0, the day before the removal in 5885280), plus the archived
telegram-otp-bot/INTEGRATION-PROMPT.md.
→ The plan's statement that "both manual-testing-plan.md and RUNBOOK.md are stale on secrets
handling" is half wrong. RUNBOOK.md is current. Phase 3 fixes one file, not two.
Holds — hardening. C-10 above. 18 of 18 unticked, four later chains, nothing reconciled.
Narrower — contract drift (C-14b). "dev/contracts/ frozen 07-13 vs server/src through 07-28"
is true by date but nearly empty in substance at the OpenAPI layer: 1 endpoint, 1 schema. The drift
that matters, if any, is in the 17 hand-written domain markdown files. Phase 2 should re-scope
accordingly.
Narrower — the design-language skill. The plan says the skill "predates that overhaul" (mobile-scoped
shell, bottom nav, react-hook-form, new icon set). It does not: SKILL.md was rewritten at baa3cc6
during iteration 1 and already covers AppFrame (4×), BottomBar, APP_FRAME_MAX_WIDTH (2×) and the
Lucide-only icon registry with @mui/icons-material removed. react-hook-form is absent from the skill,
but it is an engineering rule that client/CLAUDE.md §Forms owns — not a design-language gap. The real
gap is C-12: the skill stopped at iteration 1 while the code went on to iteration 2.
Wrong — temp/swagger.json. C-14 above. It is .gitignored and untracked, so it was never part of
the repo's documentation surface. Phase 0's brief says to delete it; that deletes a local file only.
Resolved
Each row records the decision. These decisions still need folding into docs/status/decisions.md
when phase 4 creates it — that file does not exist yet, so this table is their only home.
| # | Was | Decision | By |
|---|---|---|---|
| C-11 | The frontend-designer skill and client/CLAUDE.md both claimed the design language, with no stated precedence |
Precedence is now stated in both directions. The skill is the design contract (brand, tone, logo construction, the visual decisions, and the workflow for turning a design into a screen); docs/rules/client/ is the engineering contract and wins on every overlap — tokens, typography, the component library, shells, icons. SKILL.md's header carries the precedence statement plus a table pointing at the four files it defers to, and the overlapping detail was removed from the skill rather than duplicated. client/CLAUDE.md no longer restates design content at all. |
phase 1 |
Corrections landed by phase 1 that were not on the seeded list
Six rule statements were false against the code, not merely duplicated. Each was rewritten against
reality rather than carried over. They are recorded here because a future reader of dev/'s history will
find the old wording and needs to know it was checked.
| R- | The stale claim | Reality | Where it was |
|---|---|---|---|
| R-1 | "Use ColorSchemeScript from @/theme" |
No such export exists. The no-flash boot is CSS-only; theme/index.ts exports only ThemeProvider, getDirection, APP_THEME_* |
client/CLAUDE.md:857 |
| R-2 | A Storage.prototype.setItem intercept writes the theme cookie (3 occurrences) |
There is no such patch. ColorSchemeCookieSync — a useColorScheme() effect in ThemeProvider.tsx — writes it via setClientCookie |
client/CLAUDE.md:698, 711, 741 |
| R-3 | AppImage is part of the component library |
No such component under src/components/ |
SKILL.md:154 |
| R-4 | CONTENT_MAX_WIDTH = 800 |
It is 480, mirroring APP_FRAME_MAX_WIDTH — iteration 1 changed it and the skill was never updated (this is C-12's concrete shape) |
SKILL.md:159 |
| R-5 | AppFrame's header/<main>/footer are flex siblings, so a top bar is position: static |
Iteration 2 pinned both bars position: absolute over a single scrolling <main>, which reserves their height as padding and publishes --bal-chrome-top/-bottom |
SKILL.md:181–186 |
| R-6 | DarkModeButton.tsx is the common namespace's consumer |
Component deleted; ThemeModeSetting (a three-way segmented control in SettingsPanel) replaced it |
client/CLAUDE.md:507 |
Two counts were also wrong and are corrected in the new docs: client/CLAUDE.md:925 said "14 domains are
now REAL" and then listed 15 (verified against services/*/constants.ts: 15 real, 7 mocked), and
:61 described npm run check as "type then lint", omitting lint:copy.