Commit Graph
6 Commits
Author SHA1 Message Date
DongHyeonka 197b2c7e72 chore: pick up the regenerated Question input contract
`resolution` leaves the input's required list, so the generated type makes it
optional. The editor already sent null for an unresolved question; nothing in
the frontend changes behaviour.
2026-08-21 14:32:14 +09:00
DongHyeonka c5e8735041 feat: read the profile's topics from Studio, and add working-copy deletion
Two things an author could not control from Studio.

The profile's "주요 관심 주제" was four strings in the JSX. Creating or removing
a topic in Studio changed nothing, and correcting the list meant a rebuild and
a redeploy. It now renders the published topic list. The old literal opened
with "Backend Architecture", which no record in the catalogue actually carries
— the profile was advertising a topic that did not exist, and nothing could
have caught that while the list lived in the markup.

The working-copy list gained a delete control. It routes by kind because the
contract and the storage both do: Case and Reference share one table split by
type, Question is its own. Decision has no delete — its lifecycle is accept,
reject, supersede, which records what happened rather than erasing it — so the
control does not appear for it.

The list summary carries no version, so deletion reads the working copy first
and uses the version it finds. A stale version from a list left open should
fail as a conflict, not delete whatever is there now.
2026-08-21 13:30:37 +09:00
DongHyeonka 3754269118 feat: add the Studio release editor and point the footer at the changelog
The public site shipped a Releases page and a footer link to a release, and
neither could ever have content: the read path existed, the write path did not.
This adds the seven release operations to the contract contribution and the
gateway, and a Studio screen that can actually write one.

The editor is six markdown fields rather than one, because that is what the
contract models and what a release note is — why, what, what a reader notices,
what it leaves in the code, how it was verified, what is still open. Publishing
is separate from saving: a draft saves in any state, but the public query keys
on PUBLISHED alone, so publish is where completeness is demanded.

No new CSS. The screen reuses the document editor's field classes and the
working-copy list's row classes, so it inherits Studio's spacing and type
instead of introducing a second look.

The footer previously linked `/releases/0.1.0` — a version that did not exist,
so the link 404'd, and one that would have gone stale at 0.2.0 anyway. It now
points at the changelog index, which is the only place that knows what the
latest release is and which reads correctly when there are none.

Route inventory, navigation order, message catalog, manual accessibility
evidence, artifact baseline, and the pinned gate-shape digest all move with the
new route. The digest was recomputed by first reproducing the previous constant
from the previous gates.json, so the computation is known to be the one it was
pinned under.
2026-08-21 03:08:41 +09:00
DongHyeonka 11c2713139 feat: let Studio create the topics and projects publishing requires
Publishing needs a topic and nothing could create one. The backend now owns
that surface; this is its consumer — the management contract vendored, a
gateway over its nine operations, and one Studio screen that lists, creates,
and deletes topics and projects.

The screen adds no CSS. It reuses the classes the working-copy list already
uses, so it inherits Studio's spacing, type, and colour rather than growing a
second visual vocabulary beside them. Scope stops at list/create/delete:
renaming, phase changes, and visibility are implemented in the backend and
declared in the contract, but their screens are a separate design.

Two real defects surfaced while making the public port async, and both would
have shipped:

The search page and the header search dialog shared a query key. With an empty
query, `["tech-log","search",""]` was identical for both, so react-query
handed one surface the other's cache — different shapes — and the page died
reading a field that was not there. Keys now name the surface.

The explore filter's selects are uncontrolled and read `defaultValue`, which
React applies once. Their options arrive later now, so the first render had
nothing to match and the value stayed empty: a topic in the URL no longer
showed as selected. The form key includes whether the catalog has arrived, so
it remounts with the options present. Controlled inputs would be the other
answer, but this form submits to build a URL — the URL owns the value.

The route brought its own bookkeeping: a build chunk, a manual accessibility
evidence file, and the CI artifact baseline that counts them. The gate pins a
digest of its own shape precisely so a new route cannot slip in without that
count being reviewed.

Test harnesses that render public screens now assemble the query providers and
await the settled paint, because the screens they render became async.
2026-08-20 23:40:15 +09:00
DongHyeonka 4b62bf3b1f chore: re-vendor both contracts from the merged design package
Only the recorded source revision moves (55a9599 -> b98eaf9). The vendored
specs and the generated types are byte-identical, which is the useful part:
the envelope redefinition landed on a merge commit, not a schema change, so
nothing downstream has to move with it.
2026-08-20 18:42:04 +09:00
DongHyeonka c362ec6100 feat: vendor the public read contract, and give it its own source switch
The public surface — 17 of the 28 registered routes — reads from a 29KB
TypeScript fixture and never touches the backend. `TECH_LOG_STUDIO_SOURCE`
only ever switched the Studio gateways; `publicContent` was wired to the
static adapter unconditionally, so no configuration could make the public
site show published content. This is the first half of closing that: the
contract and the switch, with the adapter still to come.

The generator now vendors both canonical contracts instead of one. They are
independent — different services on different schedules — so each carries
its own digest and operation list, and updating one leaves the other's drift
gate quiet.

`TECH_LOG_PUBLIC_SOURCE` is deliberately a second flag rather than a rename
of the Studio one. The combination that matters right now is exactly the one
a single flag cannot express: the authoring backend is live while the public
read API does not exist yet. production stays on MOCK for that reason —
pointing it at HTTP today would empty the live site — and moves when the
backend serves /api/v1/public.

Also records the compatibility evidence the registry gate wanted for the
Studio access change in fff5e6f. That gate has been failing since, which is
on me: the change was real and breaking, and it shipped without the note
explaining that route ids and schemas are untouched and only the access
classification moves.
2026-08-20 16:19:53 +09:00