Compare commits
No commits in common. "docs/9-product-sprint" and "main" have entirely different histories.
docs/9-pro
...
main
7 changed files with 145 additions and 374 deletions
24
AGENTS.md
24
AGENTS.md
|
|
@ -1,7 +1,7 @@
|
|||
# Scholarr agent brief
|
||||
|
||||
For sprint work, read `SPRINT.md` before changing the repository. It is the
|
||||
single product, decision, provider-safety, and execution reference.
|
||||
This file is the canonical brief for every coding agent working in this repository. Read it and
|
||||
`TASKS.md` before making changes.
|
||||
|
||||
## Product
|
||||
|
||||
|
|
@ -41,11 +41,14 @@ Do not silently change a locked decision. Record a proposed change as open and a
|
|||
|
||||
## Decision record
|
||||
|
||||
`SPRINT.md` governs an explicitly owner-approved sprint. The historical charter
|
||||
and identity/calibration/testing records live in `runbooks/rewrite/`.
|
||||
On tank, the authoritative project runbook is:
|
||||
|
||||
Track acceptance and next actions in Forgejo. Record sprint implementation
|
||||
decisions in `SPRINT.md`; do not add another plan or a separate freeze queue.
|
||||
`/opt/stacks/server-docs/runbooks/scholarr-rewrite/README.md`
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -64,11 +67,10 @@ are expected.
|
|||
|
||||
## Work process
|
||||
|
||||
- Work the integration issue's acceptance items in order, against `SPRINT.md`.
|
||||
- Preparation does not authorize execution. Start only after the owner approves
|
||||
the concrete nightly plan and explicitly says to launch.
|
||||
- An approved sprint replaces separate spec-freeze ceremonies with the bundled
|
||||
defaults in `SPRINT.md`. Preserve locked requirements; report conflicts.
|
||||
- Work through `TASKS.md` in order. Each task has a goal and acceptance criteria.
|
||||
- Freeze one spec at a time. Decided items are labeled decided; open items remain open for owner
|
||||
review.
|
||||
- Do not start implementation for an area whose spec is not frozen.
|
||||
- Keep commits focused and use plain imperative commit messages without trailers.
|
||||
- Stage only files you changed.
|
||||
- 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
|
||||
when they publish, and find legal open-access copies of their work.
|
||||
|
||||
The rewrite is pre-alpha. [SPRINT.md](SPRINT.md) is the single reference for its
|
||||
proposed integrated product sprint; preparation does not authorize a launch.
|
||||
The rewrite is pre-alpha. Its product direction and interface are settled, and the architecture
|
||||
specs are being frozen before implementation begins.
|
||||
|
||||
## 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
|
||||
- Config-driven operation and simple first-run onboarding
|
||||
|
||||
Read [SPRINT.md](SPRINT.md) for scope, decisions, provider safety, and acceptance.
|
||||
[AGENTS.md](AGENTS.md) supplies the repository constraints.
|
||||
See [AGENTS.md](AGENTS.md) for the full project constraints and [TASKS.md](TASKS.md) for the
|
||||
ordered work queue.
|
||||
|
||||
## Repositories
|
||||
|
||||
- Primary: <https://github.com/JustinZeus/scholarr>
|
||||
- Forgejo tracker and sprint delivery: <https://git.justinvisser.org/justinzeus/scholarr>
|
||||
- Forgejo copy: <https://git.justintime.lol/justinzeus/scholarr>
|
||||
- Preserved legacy implementation: <https://github.com/JustinZeus/scholarr-legacy>
|
||||
|
||||
## Frozen interface contract
|
||||
|
|
@ -38,5 +38,5 @@ are the source for the Vue port, not an application implementation.
|
|||
|
||||
## License
|
||||
|
||||
The sprint proposes MIT, subject to owner approval of [SPRINT.md](SPRINT.md).
|
||||
Until a LICENSE file is added, normal copyright rules apply.
|
||||
The license has not been selected yet. Until a LICENSE file is added, normal copyright rules
|
||||
apply. License selection is an explicit project decision in [TASKS.md](TASKS.md).
|
||||
|
|
|
|||
327
SPRINT.md
327
SPRINT.md
|
|
@ -1,327 +0,0 @@
|
|||
# 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 expiry-priority 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.
|
||||
The original target was twenty-four hours. Justin selected expiry priority once
|
||||
the remaining credit window became shorter: reduce the time budget, not these
|
||||
product requirements. An unfinished gate is not completed delivery.
|
||||
|
||||
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 | At most 1,440 active minutes each, shortened for the actual go window to leave the 120-minute session allowance before the three-hour wind-down. Subscription pauses do not consume issue wall but do consume 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. |
|
||||
|
||||
The final approval packet records the requested wall and latest safe go time,
|
||||
derived from the pinned wind-down deadline minus that wall and the 120-minute
|
||||
session allowance. If owner go arrives later, do not claim the original packet
|
||||
still fits: re-evaluate timing before launch or arming. Nightly's walls are soft;
|
||||
a running role or verification may finish after cutoff, and a draining stop is
|
||||
honored between work items. This is not a process-kill deadline or a guarantee
|
||||
of redemption. Only reviewed, verified commits count as output. Quota exhaustion
|
||||
may interrupt an item and discard 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 explicitly
|
||||
requests the selected expiry-priority walls and larger token/budget guards.
|
||||
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
|
||||
final expiry guard at **2026-10-04T05:17:19.368242Z**. Justin selected a separate
|
||||
three-hour safe wind-down margin: request a safe stop of that exact run by
|
||||
**2026-10-04T02:32:19.368242Z** if it is still active. Do not redeem at wind-down
|
||||
alone when usage is below 99%; wait for the final guard and backend eligibility.
|
||||
Require a terminal run receipt and positive absence of its systemd/Docker work.
|
||||
The margin reduces expiry risk but cannot guarantee stuck verification settles.
|
||||
|
||||
The expiry guard explicitly trades remaining old-window quota for preserving
|
||||
the expiring reset. A rejected, ineligible, or ambiguous redemption must not
|
||||
select another credit, create a new UUID, or be reported as successful. Record
|
||||
and reconcile 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
Normal file
95
TASKS.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# 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,14 +1,16 @@
|
|||
# Architecture specs
|
||||
|
||||
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.
|
||||
Specs are frozen in the order listed in `TASKS.md`. Each spec must:
|
||||
|
||||
Detailed references still define data/trust boundaries, failure recovery,
|
||||
migrations, and deterministic acceptance. They do not create a second freeze
|
||||
queue for an approved sprint.
|
||||
- distinguish locked decisions from open questions;
|
||||
- define data and trust boundaries;
|
||||
- state failure, recovery, migration, and observability behavior;
|
||||
- identify deterministic fixtures and acceptance tests;
|
||||
- receive explicit owner review before being marked frozen.
|
||||
|
||||
## Detailed references
|
||||
No implementation for a subsystem starts before its spec is frozen.
|
||||
|
||||
- [Data model](data-model.md): the reviewed table contracts, proposed D1-D7
|
||||
defaults, and 22 falsifiable acceptance cases adopted by an approved sprint.
|
||||
## Current review
|
||||
|
||||
- [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
|
||||
|
||||
**Status:** historical reviewed draft; this document is not independently frozen.
|
||||
For an explicitly owner-approved sprint, [SPRINT.md](../../SPRINT.md) selects
|
||||
the proposed defaults and supplies execution authority. Preparation alone does
|
||||
not authorize implementation.
|
||||
**Status:** DRAFT FOR OWNER REVIEW, 2026-07-23. This spec is review-ready and is not frozen.
|
||||
No implementation is authorized by this document. This revision (2026-07-23) incorporates the
|
||||
Card 1 architecture review round; owner decisions are now D1 through D7.
|
||||
|
||||
This detailed reference defines persistent author/publication identity, per-user
|
||||
state, duplicate prevention, audit/undo, and legacy migration.
|
||||
This is Card 1 in [`TASKS.md`](../../TASKS.md). It defines the persistent domain model for
|
||||
global author and publication identity, per-user follows and reading state, review records,
|
||||
duplicate prevention, merge and undo, and migration from the legacy Scholarr database.
|
||||
|
||||
## Status language
|
||||
|
||||
- **DECIDED** records a project decision that this spec must not reopen.
|
||||
- **PROPOSED** records the reviewed default selected by an owner-approved
|
||||
`SPRINT.md`; without that approval it remains a proposal.
|
||||
- **OPEN Dn** identifies the historical decision entry below.
|
||||
- **PROPOSED** is the concrete default in this draft. It becomes decided only when the owner
|
||||
approves and freezes the spec.
|
||||
- **OPEN Dn** identifies an owner decision listed in [Owner decisions](#owner-decisions).
|
||||
|
||||
## 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. |
|
||||
| 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. |
|
||||
|
||||
The historical process required separate answers and an explicit freeze.
|
||||
An owner-approved [SPRINT.md](../../SPRINT.md) supersedes that process for the
|
||||
integrated sprint and adopts the defaults together. Preparation grants no execution authority.
|
||||
Freezing this spec means the owner has answered D1 through D7, approved any resulting edits, and
|
||||
explicitly changed the status at the top to `FROZEN` with the approval date. Until then, no schema
|
||||
or implementation work begins.
|
||||
|
|
|
|||
|
|
@ -141,15 +141,14 @@ adversarial dimensions over the `e36e486` draft) returned **NO-FREEZE with seven
|
|||
blockers**, all spec-text gaps rather than design flaws, and the revision
|
||||
applying every finding is committed at `a8c5939`.
|
||||
|
||||
The historical process required separate answers to **D1 through D7** and a
|
||||
spec freeze before any later work. Those issues preserve the decision evidence.
|
||||
Freezing means the owner has answered **D1 through D7** (D2 split into D2a-D2d),
|
||||
approved any resulting edits, and marked the spec frozen. **No implementation and
|
||||
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.
|
||||
|
||||
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.
|
||||
The **UI spec is already frozen** and needs no freeze card.
|
||||
|
||||
## Related
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue