Define the integrated Scholarr product sprint
This commit is contained in:
parent
41e8714ca3
commit
0e22839ae4
7 changed files with 361 additions and 145 deletions
24
AGENTS.md
24
AGENTS.md
|
|
@ -1,7 +1,7 @@
|
||||||
# Scholarr agent brief
|
# Scholarr agent brief
|
||||||
|
|
||||||
This file is the canonical brief for every coding agent working in this repository. Read it and
|
For sprint work, read `SPRINT.md` before changing the repository. It is the
|
||||||
`TASKS.md` before making changes.
|
single product, decision, provider-safety, and execution reference.
|
||||||
|
|
||||||
## Product
|
## Product
|
||||||
|
|
||||||
|
|
@ -41,14 +41,11 @@ Do not silently change a locked decision. Record a proposed change as open and a
|
||||||
|
|
||||||
## Decision record
|
## Decision record
|
||||||
|
|
||||||
On tank, the authoritative project runbook is:
|
`SPRINT.md` governs an explicitly owner-approved sprint. The historical charter
|
||||||
|
and identity/calibration/testing records live in `runbooks/rewrite/`.
|
||||||
|
|
||||||
`/opt/stacks/server-docs/runbooks/scholarr-rewrite/README.md`
|
Track acceptance and next actions in Forgejo. Record sprint implementation
|
||||||
|
decisions in `SPRINT.md`; do not add another plan or a separate freeze queue.
|
||||||
It owns doctrine, locked decisions, empirical calibration results, and project state. Its
|
|
||||||
companion files own the identity model and testing strategy. This brief is self-contained for
|
|
||||||
agents without tank access, but any state or design decision reached on tank must also be written
|
|
||||||
to that runbook in the same session.
|
|
||||||
|
|
||||||
## Frozen UI contract
|
## Frozen UI contract
|
||||||
|
|
||||||
|
|
@ -67,10 +64,11 @@ are expected.
|
||||||
|
|
||||||
## Work process
|
## Work process
|
||||||
|
|
||||||
- Work through `TASKS.md` in order. Each task has a goal and acceptance criteria.
|
- Work the integration issue's acceptance items in order, against `SPRINT.md`.
|
||||||
- Freeze one spec at a time. Decided items are labeled decided; open items remain open for owner
|
- Preparation does not authorize execution. Start only after the owner approves
|
||||||
review.
|
the concrete nightly plan and explicitly says to launch.
|
||||||
- Do not start implementation for an area whose spec is not frozen.
|
- An approved sprint replaces separate spec-freeze ceremonies with the bundled
|
||||||
|
defaults in `SPRINT.md`. Preserve locked requirements; report conflicts.
|
||||||
- Keep commits focused and use plain imperative commit messages without trailers.
|
- Keep commits focused and use plain imperative commit messages without trailers.
|
||||||
- Stage only files you changed.
|
- Stage only files you changed.
|
||||||
- Do not use em dashes in prose, code comments, commit messages, or user-facing copy.
|
- Do not use em dashes in prose, code comments, commit messages, or user-facing copy.
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -3,8 +3,8 @@
|
||||||
Scholarr is a self-hosted author watchlist and publication tracker. Follow researchers, learn
|
Scholarr is a self-hosted author watchlist and publication tracker. Follow researchers, learn
|
||||||
when they publish, and find legal open-access copies of their work.
|
when they publish, and find legal open-access copies of their work.
|
||||||
|
|
||||||
The rewrite is pre-alpha. Its product direction and interface are settled, and the architecture
|
The rewrite is pre-alpha. [SPRINT.md](SPRINT.md) is the single reference for its
|
||||||
specs are being frozen before implementation begins.
|
proposed integrated product sprint; preparation does not authorize a launch.
|
||||||
|
|
||||||
## Non-negotiable rule
|
## Non-negotiable rule
|
||||||
|
|
||||||
|
|
@ -21,13 +21,13 @@ from documented APIs such as OpenAlex, Crossref, arXiv, Unpaywall, and ORCID.
|
||||||
- Internal auth, generic OIDC, and trusted-header auth in v1
|
- Internal auth, generic OIDC, and trusted-header auth in v1
|
||||||
- Config-driven operation and simple first-run onboarding
|
- Config-driven operation and simple first-run onboarding
|
||||||
|
|
||||||
See [AGENTS.md](AGENTS.md) for the full project constraints and [TASKS.md](TASKS.md) for the
|
Read [SPRINT.md](SPRINT.md) for scope, decisions, provider safety, and acceptance.
|
||||||
ordered work queue.
|
[AGENTS.md](AGENTS.md) supplies the repository constraints.
|
||||||
|
|
||||||
## Repositories
|
## Repositories
|
||||||
|
|
||||||
- Primary: <https://github.com/JustinZeus/scholarr>
|
- Primary: <https://github.com/JustinZeus/scholarr>
|
||||||
- Forgejo copy: <https://git.justintime.lol/justinzeus/scholarr>
|
- Forgejo tracker and sprint delivery: <https://git.justinvisser.org/justinzeus/scholarr>
|
||||||
- Preserved legacy implementation: <https://github.com/JustinZeus/scholarr-legacy>
|
- Preserved legacy implementation: <https://github.com/JustinZeus/scholarr-legacy>
|
||||||
|
|
||||||
## Frozen interface contract
|
## Frozen interface contract
|
||||||
|
|
@ -38,5 +38,5 @@ are the source for the Vue port, not an application implementation.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The license has not been selected yet. Until a LICENSE file is added, normal copyright rules
|
The sprint proposes MIT, subject to owner approval of [SPRINT.md](SPRINT.md).
|
||||||
apply. License selection is an explicit project decision in [TASKS.md](TASKS.md).
|
Until a LICENSE file is added, normal copyright rules apply.
|
||||||
|
|
|
||||||
314
SPRINT.md
Normal file
314
SPRINT.md
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
# Scholarr product sprint
|
||||||
|
|
||||||
|
## Approval and authority
|
||||||
|
|
||||||
|
Justin authorized preparation, issue triage, and a new baseline commit. He has
|
||||||
|
not authorized launch. Review this document as the single product and execution
|
||||||
|
reference for the proposed 24-hour run. Approval tracking: justinzeus/scholarr#9.
|
||||||
|
Runner preparation: justinzeus/nightly#92; reset supervisor: justinzeus/nightly#93.
|
||||||
|
|
||||||
|
An explicit owner go for the concrete plan approves the proposed defaults below
|
||||||
|
and replaces the old, separate owner-freeze gates for this sprint. The agents
|
||||||
|
may then choose implementation details and record their rationale here without
|
||||||
|
stopping for another spec ceremony. They must preserve the locked requirements.
|
||||||
|
A requirement conflict, missing credential needed for a real feature, or unsafe
|
||||||
|
provider operation remains a blocker; agents must report it rather than invent
|
||||||
|
a fallback or claim completion. Preparation alone does not activate this authority.
|
||||||
|
|
||||||
|
The reset preserves Git history, the frozen design, and prior decision evidence.
|
||||||
|
It supplies one new starting commit on an isolated branch. It does not erase the
|
||||||
|
legacy repository, dirty local files, old decision issues, or production data.
|
||||||
|
|
||||||
|
## Intended result
|
||||||
|
|
||||||
|
Build a self-hosted author watchlist and publication tracker that Justin's dad
|
||||||
|
can use daily and other households can install. A reader follows researchers,
|
||||||
|
sees their publications, tracks read state, resolves ambiguous identities, and
|
||||||
|
finds legal open-access copies. An administrator can understand provider health,
|
||||||
|
recover access, audit changes, and back up or restore the installation.
|
||||||
|
|
||||||
|
The target after the run is one integrated, runnable product branch and one
|
||||||
|
reviewed PR, with executable acceptance evidence and a usable installation path.
|
||||||
|
A pile of independent PRs, a static UI demonstration, mock-only provider clients,
|
||||||
|
or a service with omitted authentication modes does not meet the target.
|
||||||
|
Twenty-four hours is a delivery target, not a promise that unfinished work is done.
|
||||||
|
|
||||||
|
The run does not deploy, merge into main, publish a release, import the real
|
||||||
|
household database, or replace the existing workflow. Justin still performs
|
||||||
|
manual UI approval and the real-data shadow period before a production cutover.
|
||||||
|
|
||||||
|
## Locked requirements
|
||||||
|
|
||||||
|
- Go backend, Vue 3 frontend, SQLite only. Use a serialized writer, foreign keys,
|
||||||
|
WAL, numbered migrations, and restart-safe persistence.
|
||||||
|
- A source-agnostic `FollowedAuthor` is the author root. Names are display/search
|
||||||
|
labels, never identity keys. Preserve external source identities and provenance.
|
||||||
|
- Share deduplicated authors and publications globally. Keep follows, read state,
|
||||||
|
visibility, and user preferences private to their user.
|
||||||
|
- Support internal username/password, generic OIDC, and trusted-header auth.
|
||||||
|
Internal auth remains the zero-dependency default and recovery path.
|
||||||
|
- Use one validated declarative configuration, environment-variable references
|
||||||
|
for secrets, safe first-admin setup, and a straightforward first-run flow.
|
||||||
|
- Use the frozen `design/DESIGN.md`, `design/tokens.css`, and bundled assets.
|
||||||
|
Implement dashboard, publication list/detail, author list/detail, review,
|
||||||
|
author search/add, bulk import, sources, activity, settings, login, onboarding,
|
||||||
|
and not-found behavior. Keep loading, empty, populated, and degraded states
|
||||||
|
honest, with responsive navigation, keyboard access, and light/dark themes.
|
||||||
|
- Serve system fonts and bundled assets locally. The design proof's
|
||||||
|
`support.js` and synthetic example records do not ship as product behavior.
|
||||||
|
- Show source freshness and confidence words rather than invented certainty or
|
||||||
|
raw match percentages. Keep new-publication counts scoped to the current user.
|
||||||
|
- Preview bulk imports and destructive admin actions before applying them.
|
||||||
|
Audit consequential writes and support conflict-checked undo.
|
||||||
|
- Implement documented clients for OpenAlex, Crossref, arXiv, Unpaywall, and
|
||||||
|
ORCID. Missing keys disable that client's network lane with an actionable
|
||||||
|
health state; they do not authorize anonymous scraping or a substitute API.
|
||||||
|
- Zero application-initiated Google contact. Scholar URLs/IDs are inert import
|
||||||
|
metadata or human-clickable links. Parse them locally; never dereference them.
|
||||||
|
No scraping, CAPTCHA solving, anti-bot evasion, or Semantic Scholar v1 expansion.
|
||||||
|
|
||||||
|
The data-model draft supplies detailed table contracts and its 22 falsifiable
|
||||||
|
acceptance cases. The frozen visual design supplies the appearance contract.
|
||||||
|
This document controls sprint scope and defaults where the old process differs.
|
||||||
|
|
||||||
|
## Proposed decisions approved together at launch
|
||||||
|
|
||||||
|
| Decision | Sprint default |
|
||||||
|
|---|---|
|
||||||
|
| D1: public identity | Internal integer joins; immutable UUIDv7 public IDs only on externally referenced or merge-aliased rows. |
|
||||||
|
| D2a: unfollow | Hide records without an active follow path; retain read/favorite state and restore it on refollow. |
|
||||||
|
| D2b: legacy favorites | Retain migrated favorite state for lossless import/export; do not add a favorite UI outside the frozen design. |
|
||||||
|
| D2c: legacy reading | Treat a publication as read if any legacy link for that user says read. |
|
||||||
|
| D2d: NEW | Derive at read time from first incremental discovery within 14 days; baseline, manual, and legacy imports are not NEW. |
|
||||||
|
| D3: attachment | Reuse exact identities, explicit provider crosswalks, and completed high-confidence calibration evidence. Review weaker or conflicting evidence; names alone never attach or merge. |
|
||||||
|
| D4: undo | No arbitrary expiry while recorded row-version preconditions hold. A conflict prevents the entire undo. |
|
||||||
|
| D5: orphans | Retain automatically. Admin garbage collection requires a backup, dry-run preview, reference checks, and audit. |
|
||||||
|
| D6: shared identity | Only administrators merge shared identity or write global negative-identity evidence. Reader decisions remain per-user. |
|
||||||
|
| D7: purge | Hard purge with no resurrection tombstone. A later sanctioned strong-ID ingest can create a new canonical row; legacy-only data cannot recreate it automatically. |
|
||||||
|
| Distribution | A self-contained Go binary with embedded built Vue assets, plus a non-root container image. Compose is an installation example, not a live deployment. |
|
||||||
|
| License | Propose MIT with Justin's personal copyright identity. Add the license only after the owner approves this packet. |
|
||||||
|
| Acquisition | OpenAlex is primary. Crossref/arXiv add work metadata; ORCID anchors identity; Unpaywall resolves legal OA locations. Persist cursors, deduplicate, and retain provenance. |
|
||||||
|
| Sync | One active run per user, shared provider budgets, idempotent resume, additive baseline import, scheduled and manual sync through the same queue. |
|
||||||
|
| Matching | Exact strong identifiers first; weak/ambiguous candidates go to review. Keep recorded calibration expectations, using public-safe fixtures only. |
|
||||||
|
| OA | Store location, license, and provenance. Expose a legal human-clickable location; do not fetch PDFs or arbitrary publisher URLs during the sprint. |
|
||||||
|
| Config changes | Validate before applying. Document restart requirements; do not add hot reload for settings whose safe transition is unclear. |
|
||||||
|
|
||||||
|
Security details follow maintained Go libraries: memory-hard password hashing,
|
||||||
|
secure expiring sessions, CSRF defenses, bounded login attempts, OIDC issuer and
|
||||||
|
signature validation with state/nonce and PKCE, and trusted headers accepted only
|
||||||
|
from explicitly trusted proxies. Reject direct-header spoofing. Isolate user
|
||||||
|
queries and authorize admin operations on the server, not only in the UI.
|
||||||
|
|
||||||
|
## Provider safety and the sprint's network boundary
|
||||||
|
|
||||||
|
Spend Codex quota on coding, testing, and reviewing, not on harvesting papers.
|
||||||
|
Automated tests and the repeatable integration smoke use deterministic local
|
||||||
|
HTTP fixtures. Production clients must be real. Justin additionally authorized
|
||||||
|
a bounded live-provider acceptance check, not bulk acquisition.
|
||||||
|
|
||||||
|
After fixture tests prove throttling and circuit breaking, an operator-owned
|
||||||
|
host check may issue at most **two requests each** to OpenAlex and Crossref,
|
||||||
|
**one each** to arXiv and Unpaywall, and **zero** to ORCID until its credentials
|
||||||
|
and non-commercial eligibility are confirmed. Six requests total, including
|
||||||
|
retries; a failure does not replenish the allowance. Use public example works,
|
||||||
|
the operator's existing contact identity, and the escrowed OpenAlex key.
|
||||||
|
Keep credentials, private calibration caches, and the household dump out of
|
||||||
|
writer sandboxes. Do not perform these checks during preparation.
|
||||||
|
|
||||||
|
Record actual endpoint, status, timestamp, and remaining allowance without keys
|
||||||
|
or full private payloads. Skip a provider whose prerequisite is missing and
|
||||||
|
report it as unverified, not passed. A 429/auth/ban response stops that provider
|
||||||
|
immediately. Honor the conservative spacing below even for this small check.
|
||||||
|
The application defaults acquisition to disabled until the operator explicitly
|
||||||
|
enables configured providers. Installation never starts a historical crawl;
|
||||||
|
confirmation queues limited work and never bypasses the scheduler.
|
||||||
|
|
||||||
|
The shipped provider scheduler must enforce:
|
||||||
|
|
||||||
|
- One outstanding request per provider and no burst allowance. Share the limit
|
||||||
|
across users, search, sync, OA lookups, retries, and manual refreshes.
|
||||||
|
- Persist the next-request time, cooldown, daily allowance consumption, and
|
||||||
|
in-progress work before issuing requests. Restart never clears a cooldown or
|
||||||
|
resets a daily budget. A daily allowance uses the provider's documented reset;
|
||||||
|
if none exists, enforce a rolling 24-hour window.
|
||||||
|
- Every attempted request, including retries, consumes the budget. Cache by
|
||||||
|
strong identifiers/query parameters; coalesce duplicate queued work. Expose
|
||||||
|
queued, cooling, failed, and disabled states without retry loops in the UI.
|
||||||
|
- A 429 immediately opens the shared provider circuit. Honor a valid
|
||||||
|
`Retry-After` date/duration, then permit at most one rate-limited probe.
|
||||||
|
Missing or invalid `Retry-After` requires an administrator-set cooldown;
|
||||||
|
do not guess a short retry interval. A second 429 in a rolling 24-hour period
|
||||||
|
requires manual re-arm after the administrator reviews the cause.
|
||||||
|
- On 401/403, a ban/challenge response, or an unexpected redirect, stop the lane
|
||||||
|
and require operator attention. Do not rotate keys, IPs, accounts, or user agents.
|
||||||
|
- Treat 503 as a provider cooldown, since some APIs use it for burst limiting.
|
||||||
|
Other transient failures get at most two retries with exponential backoff and
|
||||||
|
jitter under the same budgets. Malformed responses stop the work item; they
|
||||||
|
do not trigger an endpoint retry loop. Retain resumable work and surface errors.
|
||||||
|
- Use honest identification and configured contact details where the provider
|
||||||
|
requires them. An absent required identity or key fails closed.
|
||||||
|
- Restrict production acquisition to the provider's approved HTTPS host and path.
|
||||||
|
Reject arbitrary base URLs, Google hosts, private/link-local targets, and
|
||||||
|
unapproved redirects. Local fixture endpoints exist only in test configuration.
|
||||||
|
- Persist budgets per provider credential/IP scope; an application limiter cannot
|
||||||
|
account for other services using tank's IP. Before live enablement, the owner
|
||||||
|
must allocate that shared-IP allowance. Use the smaller of published limits,
|
||||||
|
this product's conservative default, and the allocated operator allowance.
|
||||||
|
|
||||||
|
No rate limit can guarantee that a provider will never block an IP. The zero-live
|
||||||
|
sprint budget removes this run's data-API traffic; conservative client limits and
|
||||||
|
circuit breakers constrain a later enabled installation.
|
||||||
|
|
||||||
|
### Conservative live-enable defaults
|
||||||
|
|
||||||
|
These are local engineering limits for a later owner-approved connection check,
|
||||||
|
not promised provider entitlements or the sprint's budget. The sprint budget
|
||||||
|
remains zero. One outstanding request and no burst apply to each row.
|
||||||
|
Official documentation was checked on 2026-10-02.
|
||||||
|
|
||||||
|
| Provider | Local check allowance | Access and policy constraints |
|
||||||
|
|---|---|---|
|
||||||
|
| OpenAlex | 1 request per 10 seconds; 20 attempts/day; $0.05/day spend cap | Current docs conflict about keyless access. Require an owner-managed API key and policy clarification before live enablement. Price requests by operation; request count alone is not the daily budget. Exclude content/PDF endpoints. [Authentication](https://help.openalex.org/api/authentication/), [costs](https://help.openalex.org/access/example-costs/), [deprecations](https://help.openalex.org/api/deprecations/). |
|
||||||
|
| Crossref | 1 request per 10 seconds; 80 attempts/day | Use polite `mailto` and an identifying User-Agent. Honor rate-interval and concurrency response headers; do not interpret pool counts as a fixed RPS entitlement. No published daily cap was established. [Access and authentication](https://www.crossref.org/documentation/retrieve-metadata/rest-api/access-and-authentication/). |
|
||||||
|
| arXiv | 1 request per 6 seconds; 80 attempts/day | Published terms require at least 3 seconds between calls and one connection across all machines under your control. Use the sanctioned query route, not paper scraping. Validate the approved TLS endpoint before live enablement. [Terms](https://info.arxiv.org/help/api/tou.html), [manual](https://info.arxiv.org/help/api/user-manual.html). |
|
||||||
|
| Unpaywall | 1 request per 10 seconds; 80 attempts/day | DOI lookup requires a configured email. Docs ask for at most 100,000 calls/day but state no RPS or quota-accounting key. The search route retired on 2026-09-18; do not implement it. [API](https://unpaywall.org/products/api). |
|
||||||
|
| ORCID | Disabled; conditional check: 1 request per 10 seconds, 80 reads/day | Require owner confirmation of non-commercial eligibility and an individually registered public client. Implement client-credentials OAuth and fixtures; do not automate account registration. Anonymous/public limits and 503 burst responses are not an invitation to use their burst allowance. [Limits](https://info.orcid.org/ufaqs/what-are-the-api-limits/), [registration](https://info.orcid.org/documentation/integration-guide/registering-a-public-api-client/), [public-client terms](https://info.orcid.org/public-client-terms-of-service/). |
|
||||||
|
|
||||||
|
OpenAlex's published 100-RPS ceiling and daily dollar budgets, and ORCID's
|
||||||
|
published anonymous/public read quotas, do not supersede these smaller local
|
||||||
|
allowances. ORCID Public API terms prohibit use connected with a
|
||||||
|
revenue-generating product/service. Public release must document this constraint
|
||||||
|
and leave that lane disabled unless the operator meets the terms or has an
|
||||||
|
approved Member API path. No paid provider use is authorized by this sprint.
|
||||||
|
|
||||||
|
|
||||||
|
## Build order and the acceptance ledger
|
||||||
|
|
||||||
|
Use **one integration issue**, with one verifiable behavior per acceptance box.
|
||||||
|
Nightly works its boxes sequentially on the same branch; each later item inherits
|
||||||
|
the previous verified commit. Both review roles run on each candidate. Keep the
|
||||||
|
specification in this document and evidence/ticks in Forgejo, rather than copying
|
||||||
|
another plan into each role prompt.
|
||||||
|
|
||||||
|
Work in this dependency order:
|
||||||
|
|
||||||
|
1. Runnable Go process, validated config, SQLite lifecycle, health/readiness, and
|
||||||
|
a default gate that runs offline. The first real feature supplies the Go module;
|
||||||
|
do not spend a separate item on an empty scaffold.
|
||||||
|
2. Numbered schema migrations, database invariants, public identity, and the
|
||||||
|
data-model's independent acceptance cases. Exercise migrations up/down and
|
||||||
|
serialized concurrent writes before acquisition code uses them.
|
||||||
|
3. Safe first-admin creation, internal authentication/session recovery, tenant
|
||||||
|
isolation, generic OIDC, and trusted-proxy authentication.
|
||||||
|
4. The shared persisted provider limiter and block/cooldown behavior, then one
|
||||||
|
documented provider adapter at a time against local fixtures.
|
||||||
|
5. Exact identity matching, conservative candidate review, author follow/unfollow,
|
||||||
|
publication dedup/provenance, per-user reading, and NEW projection.
|
||||||
|
6. Restart-safe queued baseline/incremental sync, partial failure, cancellation,
|
||||||
|
dry-run import, audited merge/undo, legal OA resolution, and export.
|
||||||
|
7. Vue's real API integration and the frozen screens, each a separate checked
|
||||||
|
behavior. No production fixture data and no missing buttons hidden as polish.
|
||||||
|
8. Safe installation, license, embedded assets/container packaging, offline demo,
|
||||||
|
backup/restore, operational documentation, and the final full-flow smoke.
|
||||||
|
|
||||||
|
Every implementation item states its check. Keep tests focused on observable
|
||||||
|
behavior, invariant violations, failure transitions, or a plausible regression.
|
||||||
|
Use a fake clock for cooldowns, not real sleeps. Use the race detector and parser
|
||||||
|
fuzz smoke in the final gate. Synthetic fixtures carry no real household identity.
|
||||||
|
Do not invent a claim that the private calibration corpus passed if the run did
|
||||||
|
not exercise a sanitized copy; list that validation as owner-side acceptance.
|
||||||
|
|
||||||
|
## Execution envelope
|
||||||
|
|
||||||
|
| Setting | Proposed value |
|
||||||
|
|---|---|
|
||||||
|
| Project | Scholarr only; base is the preserved sprint-baseline branch. |
|
||||||
|
| Work | One integrated product issue, ordered by the build ledger. |
|
||||||
|
| Provider/model | `openai-codex/gpt-6.1-sol` in scope, writer, escalation, spec review, and standards review. |
|
||||||
|
| Thinking | `xhigh` in all five slots. Never change this sprint to `max`. |
|
||||||
|
| Codex budget/floor | 100 window points / 0% remaining reserve; the available subscription balance is the practical bound. |
|
||||||
|
| Night/issue wall | 1,440 active minutes each; subscription pauses do not consume the issue wall, but do consume the night wall. |
|
||||||
|
| Issue token guard | 100 million non-cached tokens; finite secondary guard, not a target. |
|
||||||
|
| Role session | Existing 120-minute cap and idle detection. |
|
||||||
|
| Concurrency | One project issue; the two independent review roles may run together. |
|
||||||
|
| Repairs/refusals/resources | Existing caps, sandbox isolation, admission reserves, and broker protections. |
|
||||||
|
| Data-provider requests | Local fixtures in writer/review sandboxes; separate operator acceptance allowance of six real requests, never a crawl. |
|
||||||
|
| End | All acceptance complete, quota exhaustion, wall cutoff, or a reported safety/stuck condition. No filler work to burn remaining quota. |
|
||||||
|
|
||||||
|
Nightly checks the night wall between role sessions. A role or verification
|
||||||
|
already running may finish after the 24-hour scheduler cutoff; this plan does not
|
||||||
|
claim an exact process-kill deadline. Only reviewed, verified commits count as
|
||||||
|
output. Exhausting quota may interrupt an item and discard its uncommitted work;
|
||||||
|
completed items remain on the integration branch.
|
||||||
|
|
||||||
|
Operator-owned ceiling configuration permits the larger wall/token/budget values;
|
||||||
|
ordinary presets keep their existing defaults. The Scholarr preset requests the
|
||||||
|
larger values explicitly. Freeze the runner settings in the saved draft and keep
|
||||||
|
no model fallback. Approval builds/checks the Go and Node image without calling a
|
||||||
|
model. Launch spends subscription quota and requires Justin's explicit go on the
|
||||||
|
resolved plan, not merely permission to prepare it.
|
||||||
|
|
||||||
|
### Expiring Codex reset credit
|
||||||
|
|
||||||
|
Read-only account inspection on 2026-10-02 found 98% of the weekly window
|
||||||
|
remaining, an ordinary weekly reset at **2026-10-09T21:10:30Z**, and three saved
|
||||||
|
reset credits. The earliest credit expires at
|
||||||
|
**2026-10-04T05:32:19.368242Z**. This saved credit, not the weekly rollover, is
|
||||||
|
the urgent deadline. Re-read live usage and credit details before approval.
|
||||||
|
|
||||||
|
Preparation does not redeem a credit. A separate explicit owner go may arm a
|
||||||
|
host-only, single-use supervisor for the soonest-expiring available credit.
|
||||||
|
Use the official installed Codex app-server account/reset-credit protocol, not
|
||||||
|
cache invalidation or guessed private HTTP calls. Pin the expected account and
|
||||||
|
credit; persist one idempotency UUID before redemption. Never expose OAuth
|
||||||
|
credentials to runner images, prompts, logs, or repository files.
|
||||||
|
|
||||||
|
The proposed trigger is a quiescent run with at least 99% weekly usage, or the
|
||||||
|
expiry guard at **2026-10-04T05:17:19.368242Z** after safely stopping work.
|
||||||
|
The expiry guard explicitly trades remaining old-window quota for preserving
|
||||||
|
the expiring reset. Confirm backend eligibility first; a rejected or ambiguous
|
||||||
|
redemption must not select another credit or create a new UUID. Record and
|
||||||
|
reconcile the result using the same request ID. Redeem at most one credit;
|
||||||
|
never automatically launch a second night or spend the newly reset window.
|
||||||
|
|
||||||
|
This policy maximizes useful work, not artificial token burn. Subscription
|
||||||
|
throttling, an early finished product, or an unmet backend reset prerequisite
|
||||||
|
can prevent complete exhaustion; do not promise either exhaustion or redemption.
|
||||||
|
|
||||||
|
## Final product gate
|
||||||
|
|
||||||
|
Before marking the integration issue complete, prove:
|
||||||
|
|
||||||
|
- The built binary serves the built Vue assets and survives a SQLite reopen.
|
||||||
|
- Two users have isolated follows/read state over shared deduplicated works.
|
||||||
|
- First admin, login/logout, recovery, OIDC, and trusted-header modes behave under
|
||||||
|
both valid and hostile inputs using local identity-provider fixtures.
|
||||||
|
- An offline seeded smoke drives onboarding, search/import preview, confirmation,
|
||||||
|
baseline sync, restart/resume, incremental sync, review, undo, reading, and export
|
||||||
|
through the real HTTP interface. Tests do not make public-provider calls.
|
||||||
|
- Injected 429, banned/auth responses, daily exhaustion, redirect rejection, and
|
||||||
|
restart during cooldown prove the provider safeguards with a controlled clock.
|
||||||
|
- Migrations, race tests, parser fuzz smoke, frontend type/build gates, and a
|
||||||
|
backup/restore into a fresh data directory pass.
|
||||||
|
- README states exact setup, disabled-provider defaults, key/contact requirements,
|
||||||
|
real-provider enablement, backup/restore, and limits. The shipped app does not
|
||||||
|
contain the design tool runtime, private records, tokens, or placeholder features.
|
||||||
|
|
||||||
|
Report unmet boxes as unmet. The bounded live-provider check is part of the
|
||||||
|
sprint acceptance allowance above. Justin's visual review, private-corpus/import
|
||||||
|
validation, and the existing 2-3 week household shadow run remain acceptance
|
||||||
|
before production use; the shadow run cannot finish within this sprint.
|
||||||
|
|
||||||
|
## Owner go
|
||||||
|
|
||||||
|
The launch packet must name the pushed baseline commit, runner commit/release,
|
||||||
|
actual saved plan ID, full resolved role table and caps, image checks, and the
|
||||||
|
integration issue. It must show any failed preflight rather than hiding it.
|
||||||
|
|
||||||
|
A launch go approves this product scope and bundled defaults, the six-request
|
||||||
|
public-provider allowance, use of the available Codex subscription at `xhigh`,
|
||||||
|
and launch of that exact plan. Reset-credit redemption needs explicit approval
|
||||||
|
of the separate single-use trigger policy. Neither go authorizes a main merge,
|
||||||
|
publication, production deployment, real-data import, bulk provider crawl,
|
||||||
|
or a second night. Start launch/monitoring in a fresh session from the approval
|
||||||
|
issue after the matching runner/plugin and reset supervisor preflights pass.
|
||||||
95
TASKS.md
95
TASKS.md
|
|
@ -1,95 +0,0 @@
|
||||||
# Scholarr task cards
|
|
||||||
|
|
||||||
Work in order. A card is complete only when its acceptance criteria are met. Specs are reviewed
|
|
||||||
and frozen one at a time before implementation begins.
|
|
||||||
|
|
||||||
## Card 0: publish the clean repository **COMPLETE 2026-07-22**
|
|
||||||
|
|
||||||
**Goal:** publish this clean-history bootstrap as the public project.
|
|
||||||
|
|
||||||
The legacy GitHub repository was preserved as `JustinZeus/scholarr-legacy`. The clean bootstrap is
|
|
||||||
public at `JustinZeus/scholarr` on GitHub, with the same history copied to Forgejo. GitHub is the
|
|
||||||
primary remote.
|
|
||||||
|
|
||||||
**Acceptance:** public `justinzeus/scholarr` repositories exist on GitHub (primary) and Forgejo
|
|
||||||
(copy), both point at this clean history, no legacy history is present, and no secret or work
|
|
||||||
identity appears anywhere.
|
|
||||||
|
|
||||||
## Card 1: freeze the data model spec **REVIEW-READY 2026-07-23, NOT FROZEN**
|
|
||||||
|
|
||||||
**Goal:** specify global author/publication identity, per-user follows/read state, review records,
|
|
||||||
and merge/undo behavior.
|
|
||||||
|
|
||||||
Inputs: the project identity decision record, the legacy schema concepts worth salvaging, and the
|
|
||||||
global `FollowedAuthor` plus `AuthorSourceIdentity` direction.
|
|
||||||
|
|
||||||
**Acceptance:** `docs/specs/data-model.md` is owner-reviewed and marked frozen; constraints,
|
|
||||||
migrations, duplicate prevention, audit/undo behavior, and every remaining open question are
|
|
||||||
explicit.
|
|
||||||
|
|
||||||
**Draft result:** [`docs/specs/data-model.md`](docs/specs/data-model.md) is complete for owner and
|
|
||||||
independent review. It records fixed decisions, proposes a concrete SQLite model, and isolates five
|
|
||||||
owner decisions. Acceptance remains open until the owner reviews those decisions and explicitly
|
|
||||||
marks the spec frozen. Do not begin implementation.
|
|
||||||
|
|
||||||
## Card 2: freeze provider contracts and health
|
|
||||||
|
|
||||||
**Goal:** specify sanctioned API contracts, etiquette, persisted rate clocks/cooldowns, and
|
|
||||||
user-visible source health for OpenAlex, Crossref, arXiv, Unpaywall, and ORCID.
|
|
||||||
|
|
||||||
**Acceptance:** `docs/specs/providers.md` is owner-reviewed and marked frozen; keyed and anonymous
|
|
||||||
lanes, contact identity, retry semantics, restart behavior, fixture boundaries, and open questions
|
|
||||||
are explicit.
|
|
||||||
|
|
||||||
## Card 3: freeze the sync engine and run lifecycle
|
|
||||||
|
|
||||||
**Goal:** define safe, resumable acquisition within SQLite's concurrency model.
|
|
||||||
|
|
||||||
**Acceptance:** `docs/specs/sync-engine.md` is owner-reviewed and marked frozen; it defines one
|
|
||||||
active run per user, idempotent reruns, additive-only baseline import, resumability, writer
|
|
||||||
serialization, dry runs, and failure recovery.
|
|
||||||
|
|
||||||
## Card 4: freeze PDF resolution
|
|
||||||
|
|
||||||
**Goal:** define legal open-access discovery and provenance.
|
|
||||||
|
|
||||||
**Acceptance:** `docs/specs/pdf-resolution.md` is owner-reviewed and marked frozen; source order,
|
|
||||||
license/provenance handling, retry behavior, and the absence of all Google access are explicit.
|
|
||||||
|
|
||||||
## Card 5: freeze auth
|
|
||||||
|
|
||||||
**Goal:** define internal, generic OIDC, and trusted-header auth without provider-specific coupling.
|
|
||||||
|
|
||||||
**Acceptance:** `docs/specs/auth.md` is owner-reviewed and marked frozen; identity linking, roles,
|
|
||||||
session/security behavior, first-admin recovery, and mode-specific trust boundaries are explicit.
|
|
||||||
|
|
||||||
## Card 6: freeze the config schema
|
|
||||||
|
|
||||||
**Goal:** define one declarative configuration surface for the service.
|
|
||||||
|
|
||||||
**Acceptance:** `docs/specs/config.md` is owner-reviewed and marked frozen; defaults, validation,
|
|
||||||
secret references, provider settings, reload/restart behavior, and upgrade compatibility are
|
|
||||||
explicit.
|
|
||||||
|
|
||||||
## Card 7: freeze onboarding UX
|
|
||||||
|
|
||||||
**Goal:** make first run straightforward: search or paste Scholar URLs, review candidates, confirm,
|
|
||||||
and begin tracking.
|
|
||||||
|
|
||||||
**Acceptance:** `docs/specs/onboarding.md` is owner-reviewed and marked frozen; empty, ambiguous,
|
|
||||||
cooldown, partial-success, and unresolved-shell paths match the frozen UI and honest calibration
|
|
||||||
expectations.
|
|
||||||
|
|
||||||
## Card 8: choose the public license
|
|
||||||
|
|
||||||
**Goal:** make the repository genuinely open source under a consciously selected OSI license.
|
|
||||||
|
|
||||||
**Acceptance:** the owner selects the license, `LICENSE` is added with correct copyright identity,
|
|
||||||
and README/project metadata are updated.
|
|
||||||
|
|
||||||
## Card 9: plan build phases
|
|
||||||
|
|
||||||
**Goal:** split implementation into reviewable vertical slices against the frozen specs and UI.
|
|
||||||
|
|
||||||
**Acceptance:** build cards cover schema/migrations, providers, sync, auth, API, Vue port,
|
|
||||||
packaging, backup/restore, and release gates. Every card names its tests and demo proof.
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
# Architecture specs
|
# Architecture specs
|
||||||
|
|
||||||
Specs are frozen in the order listed in `TASKS.md`. Each spec must:
|
For the integrated product sprint, read [SPRINT.md](../../SPRINT.md) first.
|
||||||
|
The owner approves the proposed decisions together with the concrete nightly
|
||||||
|
plan; preparation alone does not authorize implementation.
|
||||||
|
|
||||||
- distinguish locked decisions from open questions;
|
Detailed references still define data/trust boundaries, failure recovery,
|
||||||
- define data and trust boundaries;
|
migrations, and deterministic acceptance. They do not create a second freeze
|
||||||
- state failure, recovery, migration, and observability behavior;
|
queue for an approved sprint.
|
||||||
- identify deterministic fixtures and acceptance tests;
|
|
||||||
- receive explicit owner review before being marked frozen.
|
|
||||||
|
|
||||||
No implementation for a subsystem starts before its spec is frozen.
|
## Detailed references
|
||||||
|
|
||||||
## Current review
|
- [Data model](data-model.md): the reviewed table contracts, proposed D1-D7
|
||||||
|
defaults, and 22 falsifiable acceptance cases adopted by an approved sprint.
|
||||||
- [Data model](data-model.md): review-ready draft dated 2026-07-23, not frozen. Owner decisions
|
|
||||||
D1 through D5 remain open.
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
# Scholarr data model
|
# Scholarr data model
|
||||||
|
|
||||||
**Status:** DRAFT FOR OWNER REVIEW, 2026-07-23. This spec is review-ready and is not frozen.
|
**Status:** historical reviewed draft; this document is not independently frozen.
|
||||||
No implementation is authorized by this document. This revision (2026-07-23) incorporates the
|
For an explicitly owner-approved sprint, [SPRINT.md](../../SPRINT.md) selects
|
||||||
Card 1 architecture review round; owner decisions are now D1 through D7.
|
the proposed defaults and supplies execution authority. Preparation alone does
|
||||||
|
not authorize implementation.
|
||||||
|
|
||||||
This is Card 1 in [`TASKS.md`](../../TASKS.md). It defines the persistent domain model for
|
This detailed reference defines persistent author/publication identity, per-user
|
||||||
global author and publication identity, per-user follows and reading state, review records,
|
state, duplicate prevention, audit/undo, and legacy migration.
|
||||||
duplicate prevention, merge and undo, and migration from the legacy Scholarr database.
|
|
||||||
|
|
||||||
## Status language
|
## Status language
|
||||||
|
|
||||||
- **DECIDED** records a project decision that this spec must not reopen.
|
- **DECIDED** records a project decision that this spec must not reopen.
|
||||||
- **PROPOSED** is the concrete default in this draft. It becomes decided only when the owner
|
- **PROPOSED** records the reviewed default selected by an owner-approved
|
||||||
approves and freezes the spec.
|
`SPRINT.md`; without that approval it remains a proposal.
|
||||||
- **OPEN Dn** identifies an owner decision listed in [Owner decisions](#owner-decisions).
|
- **OPEN Dn** identifies the historical decision entry below.
|
||||||
|
|
||||||
## Scope and boundaries
|
## Scope and boundaries
|
||||||
|
|
||||||
|
|
@ -813,6 +813,6 @@ The draft recommends one answer for each unresolved choice:
|
||||||
| D6 | Global identity authority | Restrict merges and negative-identity tombstones to an administrator role; non-admin review actions affect only that user's own follow and library rows. |
|
| D6 | Global identity authority | Restrict merges and negative-identity tombstones to an administrator role; non-admin review actions affect only that user's own follow and library rows. |
|
||||||
| D7 | Deletion vs re-ingestion | Hard purge with no resurrection tombstone; sanctioned re-ingest of the same strong identifier recreates a new canonical row, all-`legacy_import` provenance does not. |
|
| D7 | Deletion vs re-ingestion | Hard purge with no resurrection tombstone; sanctioned re-ingest of the same strong identifier recreates a new canonical row, all-`legacy_import` provenance does not. |
|
||||||
|
|
||||||
Freezing this spec means the owner has answered D1 through D7, approved any resulting edits, and
|
The historical process required separate answers and an explicit freeze.
|
||||||
explicitly changed the status at the top to `FROZEN` with the approval date. Until then, no schema
|
An owner-approved [SPRINT.md](../../SPRINT.md) supersedes that process for the
|
||||||
or implementation work begins.
|
integrated sprint and adopts the defaults together. Preparation grants no execution authority.
|
||||||
|
|
|
||||||
|
|
@ -141,14 +141,15 @@ adversarial dimensions over the `e36e486` draft) returned **NO-FREEZE with seven
|
||||||
blockers**, all spec-text gaps rather than design flaws, and the revision
|
blockers**, all spec-text gaps rather than design flaws, and the revision
|
||||||
applying every finding is committed at `a8c5939`.
|
applying every finding is committed at `a8c5939`.
|
||||||
|
|
||||||
Freezing means the owner has answered **D1 through D7** (D2 split into D2a-D2d),
|
The historical process required separate answers to **D1 through D7** and a
|
||||||
approved any resulting edits, and marked the spec frozen. **No implementation and
|
spec freeze before any later work. Those issues preserve the decision evidence.
|
||||||
no Card C.2 work begins before that freeze.** That rule is not enforced by
|
|
||||||
convention any more: the implementation issue carries real Forgejo dependencies
|
|
||||||
on all seven decision issues, so Forgejo itself refuses to close it while any
|
|
||||||
decision is open.
|
|
||||||
|
|
||||||
The **UI spec is already frozen** and needs no freeze card.
|
For an explicitly owner-approved integrated sprint, `SPRINT.md` replaces the
|
||||||
|
separate freeze gates and adopts its proposed defaults together. The owner must
|
||||||
|
approve the concrete nightly plan and explicitly authorize launch first.
|
||||||
|
Preparation and a baseline commit alone do not authorize implementation.
|
||||||
|
|
||||||
|
The **UI spec is already frozen**. Its appearance contract still binds the sprint.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue