feat(portal): SPEC-014 v0.8 provider portal (Phases 1A+1B+1C, 3-round audit gate)#110
Merged
Conversation
Lands the seller-facing Provider Portal spec, its audit artifact, the spec-build/spec-audit prompts that produced it, and the three IMPL build prompts that drive the implementation: - SPEC-014-provider-portal.md v0.8 (8 audit rounds: 3 HIGH + 2 MEDIUM applied, remaining MEDIUM/MINORs accepted as v0.2 backlog per operator instruction) - SPEC-014-audit.md (latest codex audit output) - BUILD_SPEC_014_PROMPT.md / AUDIT_SPEC_014_PROMPT.md (the spec- writing + spec-audit prompts; v0.12 / round 11) - BUILD_SPEC_014_IMPL_PHASE_1A/1B/1C_PROMPT.md (new) — one IMPL build prompt per §11 phase: 1A scaffolding + AUTH-3 + sign-in + Surface A; 1B Surface B (Setup & Updates + GitHub Releases feed with CORS/rate-limit fail-loud); 1C Surfaces C/D/E + sidebar polish + check-bundle.sh AC 8(b)+8(f) grep guard. Each IMPL prompt is self-contained (constraints, required reading, exact edits, done criteria, out-of-scope, self-check command) per the project's BUILD_SPEC_NNN_IMPL_PHASE_* convention. Phases land sequentially: each goes through its own IMPL audit gate (specs/AUDIT_SPEC_014_IMPL_PHASE_*_PROMPT.md, drafted later) and ships as its own PR per memory: feedback-build-audit-loop. No implementation code lands in this commit — the scaffold at frontdoor/provider-portal/ remains .gitkeep + one-line README. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1A of SPEC-014 v0.8 per BUILD_SPEC_014_IMPL_PHASE_1A_PROMPT.md: single-file bundle with AUTH-3 fail-CLOSED loader, AUTH-1 sign-in (in-memory only), AUTH-2 401/403/404 handling + stale-config guard, Surface A (header + counters + needs-attention), sidebar shell with B/C/D/E stubbed, same-origin proxy fail-loud banner, mobile collapse, SPEC-009 §6 visual tokens verbatim. Files: - frontdoor/provider-portal/index.html (NEW, ~38 KB single-file bundle, inline JS+CSS, no build step, no CDN) - frontdoor/provider-portal/portal-config.json.example (NEW) - frontdoor/provider-portal/README.md (extended from one-line scaffold to operator setup + reverse-proxy contract) - specs/AUDIT_SPEC_014_IMPL_PHASE_1A_PROMPT.md (NEW; the IMPL audit prompt fired against this commit) - specs/SPEC-014-impl-audit.md (NEW; codex round 1 audit output: 0 CRITICAL / 2 HIGH / 0 MEDIUM / 0 MINOR / 1 QUESTION) Status: NOT audit-clean — 2 HIGH findings open per the round 1 audit (require_provider_tokens=false page unreachable; stale-config second-failure notice unreachable through actual sign-in flow). Per memory: feedback-build-audit-loop, fix rounds + re-audit continue on this branch before PR. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Revises the three IMPL build prompts so Phases 1A/1B/1C all land on `feat/spec-014-provider-portal` and ship as ONE PR (not three). Audit gates between phases still bind — but they stop forward progress *within* the branch (commit fixes until 0 CRITICAL/HIGH/ MEDIUM, then move to the next phase), not as separate PRs. Changes: - BUILD_SPEC_014_IMPL_PHASE_1A_PROMPT.md: branch is `feat/spec-014-provider-portal` (was `feat/spec-014-portal- phase-1a`); operator notes spell out the single-PR flow. - BUILD_SPEC_014_IMPL_PHASE_1B_PROMPT.md: prerequisite reworded from "Phase 1A merged to main" to "Phase 1A LOCKED on the same branch"; explicit "do NOT branch or push" instruction. - BUILD_SPEC_014_IMPL_PHASE_1C_PROMPT.md: same as 1B; operator notes describe opening the single PR after Phase 1C LOCKs. Rationale: SPEC-014 v0.1 is a single read-side web surface; the phased structure is for audit discipline, not for separable shipping units. The original three-PR plan would have forced three review cycles for one cohesive surface. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Round 2 codex audit verifies closure of the round-1 HIGH findings: - A.2 (require_provider_tokens non-true unreachable flag-false page): CLOSED. validateConfig() now treats any non-true value (false, "true", 1, null) as flag-false and routes it past the generic configError rendering to renderUnavailable_FlagFalse(), which cites SPEC-002 FR-P12 and SPEC-005 §11.5. In-memory fetch-spy harness confirmed all four non-true values issue only /portal-config.json and make zero subsequent network calls. - C.4 (stale-config notice unreachable through real UI): CLOSED. submitSignIn() no longer resets state.authFailBySurface on retry; the counter is reset only on a successful authenticated 2xx (per-surface) and on explicit signOut(). Two-retry harness confirmed first 403 shows only the identical sign-in rejection copy and second consecutive 403 on the same surface adds the misconfig notice while preserving the rejection copy. Round 2 sweep of Categories A + C found 0 new findings. Round 1's QUESTION (B.5 — autocomplete=off on the provider_token input) is accepted as the deliberate privacy-preserving default per the recommendation; not re-litigated. Phase 1A readiness: READY TO COMMIT (already committed in 3cd7787 with the fixes folded in; this commit closes the audit-loop artifact trail). Artifact: specs/SPEC-014-impl-audit.md (round-2 section appended). Re-audit prompt: specs/AUDIT_SPEC_014_IMPL_PHASE_1A_R2_PROMPT.md (new file; same shape as SPEC-013 IMPL R2 prompts). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1B of SPEC-014 v0.8 per BUILD_SPEC_014_IMPL_PHASE_1B_PROMPT.md.
Extends the Phase 1A bundle with Surface B (Setup & Updates):
- B.1 requirements grid (4 cards, SPEC-003 §5 / FR-D1 verbatim).
- B.1a RAM-to-model sizing card (FR-D2 + FR-D2.1; presented as
a hint with footer note).
- B.2 numbered setup steps (3 cards, each a copy-to-clipboard CTA):
- Step 1 install.sh one-liner (FR-C2).
- Step 2 macprovider-cli status (FR-C4 / §6.2).
- Step 3 macprovider-cli autotune (SPEC-013 §6 / NFR-4) — no
autotune banner, only the CTA.
- B.3 GitHub Releases feed:
- 5 min in-memory cache TTL, fetch on first Surface B mount.
- X-RateLimit-Remaining: 0 → loud fallback notice; previously
cached list continues to render.
- fetch() rejection (CORS / network) → loud user-visible notice
naming SPEC-014 Open Q2.
- Non-2xx → HTTP <N> from GitHub Releases notice.
- Up to 12 entries; tag_name (fallback name) in monospace,
published_at sliced to YYYY-MM-DD, <details><pre> for
plain-text body (no markdown render of remote content,
no innerHTML of remote data), per-entry copy-to-clipboard
CTA for macprovider-cli update.
- No "currently installed" badge (DEFERRED to v0.2, Open Q5).
- Never reads Access-Control-Allow-Origin as application data.
- B.4 coordinator broadcasts panel NOT RENDERED (DEFERRED, Open Q5).
Phase 1A invariants preserved: AUTH-3 fail-CLOSED loader, AUTH-2
401/403/404 identical copy, stale-config guard, same-origin
coordinator routes, in-memory-only session, no remote command
execution. Sign-out now also clears state.releases.
Files:
- frontdoor/provider-portal/index.html (extended ~200 lines for
renderSetup + releasesFetch + B-section CSS).
- frontdoor/provider-portal/README.md (phase-status update).
- specs/AUDIT_SPEC_014_IMPL_PHASE_1B_PROMPT.md (NEW; IMPL audit
prompt fired against this commit).
- specs/AUDIT_SPEC_014_IMPL_PHASE_1B_R2_PROMPT.md (NEW; round-2
closure-verification prompt).
- specs/SPEC-014-impl-audit.md (appended with Phase 1B round 1
and round 2 sections).
IMPL audit:
- Round 1: 0 CRITICAL / 0 HIGH / 2 MEDIUM / 0 MINOR / 0 QUESTION.
- C.4: Step 3 autotune cite incorrectly referenced SPEC-003 §6.2
(which lists serve/self-test/status/update/uninstall, NOT
autotune). Fix: keep only SPEC-013 §6 / NFR-4.
- H.1: Phase 1B CSS added raw #0a0b0e literals for .snippet code
and .release-card pre.rel-body. Fix: replace with var(--bg).
- Round 2: both MEDIUM CLOSED, 0 new findings.
- Phase 1B readiness: READY TO COMMIT (this commit).
Per memory: feedback-build-audit-loop. Per BUILD prompt's
single-PR plan, Phases 1A/1B/1C all land on the same branch
(feat/spec-014-provider-portal) and ship as a single PR after
Phase 1C LOCKs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1C of SPEC-014 v0.8 per BUILD_SPEC_014_IMPL_PHASE_1C_PROMPT.md.
Lands the final three surfaces and the AC 8(b)+8(f) build-time
grep guard. With this commit, SPEC-014 v0.1 is feature-complete on
this branch — ready to open the single PR after audit gates land.
Surface C (Earn):
- C.1 credit totals row (3 cards) reusing state.earn from Phase 1A:
Lifetime credits / Current window / Last payout-ready window.
Integer credits per SPEC-005 §11.4; no fiat conversion, no $,
no "withdrawable balance" label.
- C.2 fiat-payout card: literal text "Fiat payout rail not yet
specified — future spec." citing SPEC-005 §1.3 / §2.1 D1 +
Open Q3. No country selector, no Stripe, no bank-link CTA, no
payout-now CTA.
- C.3 / C.4 NOT rendered (Open Q4 deferred).
- Sidebar mount on `earn` route warms earnFetch("earn") so the
Phase 1A stale-config guard increments correctly on this surface.
Surface D (Monitoring):
- ONE placeholder card titled "Monitoring — coming after SPEC-002
amendment" with three bullets naming Open Q5 + privacy-redaction
policy. ZERO network requests issued by the renderer.
Surface E (Identity):
- ONE read-only card with four fields: provider_id (pasted),
Tier (pool data), State (pool data), Coordinator base URL
(state.cfg.coordinator_base_url). Footer cites hardware /
runtime fields as deferred behind Open Q5 and rotation /
removal behind Open Q6. NO rotate-token button, NO remove-
machine button, NO notification opt-in toggles, NO hardware
field. Mount issues NO new fetch.
Sidebar polish:
- Sign-out (already wired in Phase 1A + 1B) clears session,
pool, earn, releases, authFailBySurface; stops all timers;
re-renders to sign-in. Verified.
- API Docs link uses rel="noopener noreferrer" target="_blank".
- Mobile (<720 px) hamburger / scrim unchanged from Phase 1A.
check-bundle.sh (NEW):
- Build-time grep guard for AC 8(b) (privileged-key routes +
privileged-key identifier) + AC 8(f) (single-machine copy
hygiene). Exits 0 clean / 1 found / 2 missing.
- Self-protection: EVERY prohibited literal in the script source
(comments, variable assignments, echo messages, regex
assignments) is split via Bash string concatenation so an
external scan with the same grep patterns the script enforces
against the bundle produces zero matches in the script itself.
This pattern is now uniform across op_routes, op_key_pat,
multi_machine, and the failure-message labels.
- README documents the script + CI hook + deploy steps.
IMPL audit:
- Round 1: 0 CRITICAL / 0 HIGH / 1 MEDIUM / 0 MINOR / 0 QUESTION.
- E.5: check-bundle.sh source contained literal `operator-key` /
`operator_key` in comments and echo messages (only the op_routes
array was split; the comments / regex assignment / failure
messages were not). Fix: split every prohibited literal via
string concatenation; rename failure-message labels to
`priv""ileged-key route` / `priv""ileged key identifier`.
- Round 2: E.5 CLOSED, 0 new findings. check-bundle.sh PASSES.
Single-PR readiness: READY TO OPEN PR.
Files:
- frontdoor/provider-portal/index.html (extended ~150 lines for
renderEarn + renderMonitoring + renderIdentity + Surface C/D/E
CSS).
- frontdoor/provider-portal/check-bundle.sh (NEW, 85 lines,
executable).
- frontdoor/provider-portal/README.md (extended with deploy steps,
CI hook, auth model summary, Phase 1C status).
- specs/AUDIT_SPEC_014_IMPL_PHASE_1C_PROMPT.md (NEW; IMPL audit
prompt fired against this commit).
- specs/AUDIT_SPEC_014_IMPL_PHASE_1C_R2_PROMPT.md (NEW; round 2
closure verification prompt).
- specs/SPEC-014-impl-audit.md (appended with Phase 1C round 1
and round 2 sections).
Per memory: feedback-build-audit-loop. The single PR opens after
this commit lands and `feat/spec-014-provider-portal` is ready to
push.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
frontdoor/provider-portal/index.html— AUTH-3 fail-CLOSED loader, AUTH-1 sign-in (in-memory only), AUTH-2 401/403/404 identical copy + stale-config guard, same-origin proxy fail-loud, all five surfaces (Machine, Setup & Updates, Earn, Monitoring placeholder, Identity).check-bundle.shbuild-time grep guard for AC 8(b) (privileged-key route + identifier isolation) + AC 8(f) (single-machine copy hygiene). Self-protected via Bash string concatenation so external scans with the same patterns produce zero matches on the script itself.What ships
/v1/pool/check(same-origin) +/providers/{id}/earnings(same-origin)/providers/{id}/earnings(reuses Phase 1A cache)state.pool.datalazy readPrivacy / security invariants enforced
localStorage/sessionStorage/document.cookie/indexedDB— session is in-memory only; page reload returns to sign-in.sendBeacon.operator_keyever prompted, parsed, or transmitted.require_provider_tokensstrict-trueonly; any other value (false, "true", 1, null) renders the SPEC-002 FR-P12 + SPEC-005 §11.5 explanation and makes zero further network calls.bodyrendered viadocument.createTextNodeonly — noinnerHTMLof remote data.Access-Control-Allow-Originas application data.Commit history on this branch
Audit-loop findings closed across the three phases:
check-bundle.shself-matched theoperator-keyregex from its own comments + echo messages). Round 2 LOCK.Test plan
frontdoor/provider-portal/check-bundle.shexits 0 onmainafter merge..github/workflows/step runningcheck-bundle.shon any PR touchingfrontdoor/provider-portal/**(SPEC-014 §8(b) + §8(f) CI gate).portal-config.json+ reverse proxy + verify before pointing DNS.v0.2 reopens after Open Qs (Q2 / Q3 / Q4 / Q5 / Q6 / Q7 / Q8 / Q9 / Q10 / Q11) land their owning-spec amendments.
🤖 Generated with Claude Code