Monorepo, core with seams, read-only rail, fail-closed detector, headed login,
redacted capture, CLI previred auth login|status|logout. Done.
One real login with --capture, then pin the authenticated marker and update the
contract doc (see CURRENT_STATUS.md). Nothing downstream can work until this
lands, so it is the whole of the next increment.
UF, UTM, topes imponibles, tasas AFP / salud / SIS / AFC by período.
Deliberately first among the data surfaces because it is public and
session-less — the direct analogue of the sii project's requestPublic seam
(its ADR-014). It needs no marker, no session and no read-only rail, so it proves
the facade → task → CLI path end-to-end independently of the bootstrap above, and
it is immediately useful next to the SII tooling for payroll arithmetic.
Landed 2026-08-10 (ADR-010): PortalDriver.requestPublic, an in-house HTML
table reader, portal/indicadores, tasks/indicadores, and
previred indicadores. Verified live against the real page.
No --periodo, contrary to the original sketch: the page publishes one
period and historical months exist only as PDFs with unconstructable URLs. The
flag would have been a promise the data cannot keep.
What was declared and paid, by período: montos, fecha de pago, instituciones. The core reconciliation surface against payroll — and against what SII sees.
Reassessed 2026-08-11 after mapping the empresa home (see
docs/previred-contract/portal-map.md). This is NOT the cheap next step it
looked like. Previred does not separate "look at what I paid" from "pay":
planillas pagadas and certificado de trabajadores both live behind the
Pago Cotizaciones → Remuneraciones tile, i.e. inside a payment area, which
dispatches by form BODY (web_accion, web_prg_destino, prgSalida) rather
than by URL reqName.
So increment 4 now has a hard prerequisite:
4a. Finish the rail first. ✅ Done 2026-08-12: assertDispatchable(body)
guards reqName / web_prg_destino / prgSalida against the allowlist, checks
web_accion against the write tokens, and sweeps every other value for a
smuggled prg… write program. Pinned by test against the real program names.
Not yet wired, because no form-POST primitive exists — which is the point: the
guard landed first, so requestForm cannot ship unguarded.
4b. Done 2026-08-12. Programs AND response shape are both known — see
docs/previred-contract/planillas-pagadas.md. The search is
prgconsultaplanillaspagadas; PDFs come from a second controller,
/wEmpresas/CtrlPdf. The response is one table grouped by institution type
(SEGURO SOCIAL / AFP / ISAPRE / MUTUAL / IPS-FONASA) with folio + total per
institution and a subtotal per group.
4c. Landed 2026-08-12. PortalSession.requestForm (guarded by both rail
gates), the three read programs allowlisted with the pin test updated,
portal/pagos parsing the grouped table, tasks/pagos, and
previred pagos --periodo YYYYMM. Aggregate-level only; per-worker rows stay
gated by ADR-008.
⚠ The parser is verified against a synthetic fixture; the LIVE round-trip is not. Everything up to the POST is tested — form state carried forward, the rail refusing a swapped program, the response parsed — but no authenticated request has actually been issued to Previred. First real run may surface a missing field in the request body.
Lands once increments 3–4 give an assistant something worth calling. Mirrors the
sii MCP shape: thin tools over the same core tasks, so the read-only rail and
audit receipt apply unchanged.
Binary downloads. Requires a requestBinary seam whose body is never
text-decoded — the same treatment as the sii project's ADR-042.
- Nómina de trabajadores — blocked on ADR-008 (third-party PII posture).
Employee RUTs, salaries and health-insurance affiliation are other people's
data, a category this project's
siipredecessor never handled. Do not build this facade until that ADR is Accepted.
- Declaración and pago. Enforced, not merely intended: the read-only rail (ADR-004) refuses these programs at runtime and a test pins the refusal. Changing that requires an ADR that supersedes ADR-004.