From f46319b1b295a75a6b40aeed5616f5115dbd0f68 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Sun, 9 Aug 2026 15:22:41 +0800 Subject: [PATCH 1/7] docs(governance): record completed workflow rollout - capture Phase 4 and Phase 5 implementation evidence - record Phase 6 settings, validation runs, audit, and rollback --- .../evidence/phase-6/README.md | 69 +++++++++ .../implementation-plan.md | 142 +++++++++++++++--- tasks/organization-git-workflow/packet.md | 8 +- 3 files changed, 199 insertions(+), 20 deletions(-) create mode 100644 tasks/organization-git-workflow/evidence/phase-6/README.md diff --git a/tasks/organization-git-workflow/evidence/phase-6/README.md b/tasks/organization-git-workflow/evidence/phase-6/README.md new file mode 100644 index 0000000..9595bb0 --- /dev/null +++ b/tasks/organization-git-workflow/evidence/phase-6/README.md @@ -0,0 +1,69 @@ +# Phase 6 Evidence + +Phase 6 closes repository-level Actions drift and performs the final governance audit. It does not +change product code or repository-specific CI/CD topology. + +## Pre-mutation snapshot + +- Captured: 2026-08-09 through read-only GitHub REST requests against `InKCre/core-py`, + `InKCre/client-web`, `InKCre/ui`, and `InKCre/docs`. +- Every checked-in external `uses:` reference is a full commit SHA with a readable version comment. + Local composite actions use local paths. +- Every checked-in workflow declares its token permissions; write grants are limited to the jobs + that publish packages, create release pull requests, or deploy artifacts. +- All repositories currently allow all Actions and report platform SHA enforcement disabled. +- `core-py`: default token `write`; Actions PR approval enabled; fork approval `first_time_contributors`. +- `client-web`: default token `read`; Actions PR approval disabled; fork approval + `all_external_contributors`. +- `ui`: default token `read`; Actions PR approval enabled; fork approval `first_time_contributors`. +- `docs`: default token `write`; Actions PR approval enabled; fork approval + `first_time_contributors`. +- Main protection and merge methods already match the baseline in all four repositories. +- Production is restricted to `main` in `core-py`, `ui`, and `docs`. The `client-web` production + environment has no deployment branch policy. + +## Approved target + +- Default token permission is `read` in all four repositories. +- Actions cannot approve pull-request reviews in any repository. +- Every external fork contributor requires maintainer approval before read-only, secret-free CI. +- Platform SHA enforcement is enabled in all four repositories. +- `client-web` production accepts only `main`. +- Selected-actions allowlists remain optional and are not required for Phase 6 completion. + +## Applied settings and verification + +- All four repositories now report default token permission `read`, Actions PR approval disabled, + fork approval `all_external_contributors`, `allowed_actions=all`, and platform SHA enforcement + enabled. +- `client-web` production now has exactly one custom branch policy: branch `main`. +- Secret-free manual validation completed successfully on the exact current main revisions: + - `docs` `Website checks` run `31300335436` at `4c44cf16`; + - `ui` `UI checks` run `31300376700` at `f78df4c`; + - `core-py` `Repository and artifact checks` run `31300460833` at `531b0d28`; + - `client-web` `Client checks` run `31300570405` at `37546a51`. +- The final remote scan found no unpinned external action reference: `core-py` has 13 external and + five local references; `client-web` has 46 external references; `ui` has 31; `docs` has 26. +- Required checks remain strict and bound to GitHub Actions app `15368`; main protection or the UI + rulesets retain required pull requests, zero approvals, resolved conversations, linear history, + no bypass, no force push, and no deletion. +- Merge commits remain disabled while squash and rebase remain available. Every production + environment now accepts only `main`. +- The organization contribution files still resolve with the intended local overrides: core-py owns + its implementation-specific contribution guide, client-web owns its application-specific pull- + request template, and the organization defaults cover missing files. + +## Remaining publication + +- The local `InKCre/.github/GOVERNANCE.md` change states the all-external fork approval rule and the + actual environment-job credential boundary. It requires explicit commit and push authorization. +- The task packet and this evidence remain local until explicitly authorized for commit and push. + +## Rollback + +- Restore each repository's captured default token, PR approval, fork approval, Actions allow, and + SHA-enforcement values independently. +- Remove only the newly created `client-web` production branch policy if that policy blocks the + documented main deployment controller. +- Stop the rollout before mutating the next repository when a secret-free validation workflow cannot + start. Do not weaken branch protection or expose preview/production credentials to recover CI. diff --git a/tasks/organization-git-workflow/implementation-plan.md b/tasks/organization-git-workflow/implementation-plan.md index 573db63..671d669 100644 --- a/tasks/organization-git-workflow/implementation-plan.md +++ b/tasks/organization-git-workflow/implementation-plan.md @@ -1,6 +1,6 @@ # Organization Git and GitHub Workflow Implementation Plan -- **Status**: Phases 0-3 are complete. Phase 3 landed the core schema-bearing stable release foundation, the client stable resolver/typegen/real-service E2E contract, separate PR preview and exact-main delivery controllers, direct required-check names, and the accepted repository settings. Client PR #49 was admitted through a real one-entry merge queue; merge-group run `31256717813` passed all four required checks on synthetic commit `cc857104`, which became main. The temporary queue rule was then removed so GitHub does not force one merge method; squash remains the default and rebase remains available. Preview cleanup run `31256442627`, automatic cleanup run `31256837743`, and production runs `31256434332`/`31256836894` passed. Deferred full-stack preview bootstrap and live external-fork package-access probing are recorded rather than represented as finished. The reverted synchronized product series is preserved without conflicts in draft client-web #50 and core-py #45. Immediate fan-out, dependency-update pull requests, and a cross-repository GitHub App remain rejected. Phases 4-6 remain proposed and unauthorized. +- **Status**: Phases 0-5 are complete. Phase 3 landed the core schema-bearing stable release foundation, the client stable resolver/typegen/real-service E2E contract, separate PR preview and exact-main delivery controllers, direct required-check names, and the accepted repository settings. Client PR #49 was admitted through a real one-entry merge queue; merge-group run `31256717813` passed all four required checks on synthetic commit `cc857104`, which became main. The temporary queue rule was then removed so GitHub does not force one merge method; squash remains the default and rebase remains available. Preview cleanup run `31256442627`, automatic cleanup run `31256837743`, and production runs `31256434332`/`31256836894` passed. Deferred full-stack preview bootstrap and live external-fork package-access probing are recorded rather than represented as finished. The reverted synchronized product series is preserved without conflicts in draft client-web #50 and core-py #45. Phase 4 independently aligned core-py governance through merged PR #46 and live-proved main CI, immutable publication, production admission, `stable`, and cleanup before removing its temporary worktree. Immediate fan-out, dependency-update pull requests, and a cross-repository GitHub App remain rejected. Phase 5 resolved release PR #34 and package `1.3.1`, published the check-name governance, merged runner-CD foundation PR #35 and sequential cutover PR #36, disabled Cloudflare automatic Git builds after dual-run proof, live-proved runner preview/cleanup and exact-main same-run production delivery, adopted `ui-web checks`, serialized release runs, and completed the UI ruleset/merge-method transition. Phase 6 has applied and live-verified the four repositories' read-only workflow defaults, disabled Actions review approval, all-external fork approval, full-SHA enforcement, and the missing client-web production-main policy. Final settings and workflow audits pass; only explicit publication of the local governance and task-packet records remains. - **Control surface**: [`packet.md`](./packet.md) owns current task state and decisions. This file owns execution order, planned mutations, verification, and rollback. - **Durable owner**: `InKCre/.github` will own the organization Git and GitHub workflow standard. Repository code, tests, release mechanics, and profile-specific instructions remain with each repository. - **Enforcement scope**: `InKCre/core-py`, `InKCre/client-web`, `InKCre/ui`, and `InKCre/docs`. `InKCre/.github` is included only as the governance control plane. @@ -34,7 +34,21 @@ - Reject force pushes, deletion, and merge commits; require linear history. - Allow squash and rebase merges. Document squash as the normal choice and rebase as an intentional choice for curated commit series or stack maintenance. - Leave automatic branch deletion disabled while the stacked-PR workflow is being learned. -- Preserve repository-specific check names and bind checks to the GitHub Actions app when the setting supports a source identity. +- Name reported check contexts ` ` in lowercase. Use the repository name as `scope` + unless a monorepo job validates one independently understood application or package, in which case + use that application or package name. Validation kinds are `checks`, `tests`, and `e2e tests`: + `checks` is the aggregate code/build/package contract; use `tests` or `e2e tests` when those lanes + report independently. Delivery jobs use `preview`, `preview cleanup`, and `deployment`. Keep + implementation adjectives and internal architecture terms in step names and documentation rather + than public check names. +- Treat required-context names as stable GitHub API. Bind them to the GitHub Actions app when the + setting supports a source identity, and migrate each existing repository only alongside a real + workflow change. Phase 5 applies this convention to `ui-web checks`; existing names in + `client-web`, `client-webext`, `core-py`, and `docs` remain unchanged until their next justified + workflow slice. +- Repository-owned check contexts follow this naming convention. Provider-owned integration checks, + such as Cloudflare's fixed `Cloudflare Pages` context, retain the provider name and are documented + separately; do not create a duplicate wrapper job merely to rename them. - Treat an emergency rule change as audited break-glass work: state the reason and affected repository, obtain explicit authorization, restore the baseline immediately after recovery, and follow with a pull request. Do not maintain a routine administrator bypass. ## Stacked Pull-Request Convention @@ -56,6 +70,7 @@ - status-check and conversation semantics; - squash-default, rebase-allowed merge policy; - stacked pull-request procedure; + - stable ` ` public check naming for validation and delivery jobs; - candidate validation, preview delivery, release authority, and artifact identity; - GitHub Free enforcement and exception handling. - Add `CONTRIBUTING.md` as the concise contributor entrypoint. Link to `GOVERNANCE.md` and require repository-local verification commands. @@ -149,13 +164,52 @@ GitHub uses defaults from a public `.github` repository only when a repository h ### `InKCre/ui` Package Profile -- Keep `.github/workflows/ci.yml` as pull-request validation with required context `Reproducible workspace check` and Changesets validation. +- Keep `.github/workflows/ci.yml` as pull-request validation and rename its aggregate required + context from `Reproducible workspace check` to `ui-web checks`. The job continues to own the + frozen workspace, generated-output, type, unit-test, build, packed-package, story, and Changesets + contracts; the simpler context name describes its public scope rather than every internal step. - Keep `.github/workflows/release-and-publish.yml` as the only package release authority on `main`: - serialize runs with one `ui-release-main` concurrency group; - use `cancel-in-progress: false` so a publication is never cancelled halfway; - continue using the built-in `GITHUB_TOKEN` as `NODE_AUTH_TOKEN` for GitHub Packages. +- Migrate Histoire delivery from Cloudflare's Git pull integration to repository-owned runner push + without replacing the existing `inkcre-web-design` Pages project or its custom domain: + - export the current Pages project/build/domain/branch-control configuration before editing; use + `wrangler pages download config` in a disposable location or the authenticated Cloudflare API, + review the result, and never copy credentials into the repository; + - pin Wrangler in the workspace and check in a reviewed Pages configuration derived from the + existing project, with project name and `packages/web/.histoire/dist` output owned as code; + - keep `ui-web checks` secret-free and upload its exact successful Histoire output as a short-lived + artifact; + - add a trusted `workflow_run` preview controller that accepts only an open, same-repository pull + request targeting `main` at the exact checked head, downloads that artifact without executing + pull-request code, and pushes it to deterministic Cloudflare branch `pr-N`; + - add a trusted close controller that replaces the exact `pr-N` alias with a small `noindex` + tombstone; accept that older immutable Cloudflare deployments remain reachable; + - add a protected-main delivery workflow that checks out exact `main`, performs a focused Histoire + release build, transfers the same-run artifact to a production job, pushes it with Wrangler, and + smokes `https://design.inkcre.dev`; + - use repository `preview` and `production` environments, select the existing organization + Cloudflare credentials for `ui`, and set repository variable `CLOUDFLARE_PAGES_PROJECT` to + `inkcre-web-design`; separate preview and production tokens are not required for this static site; + - name repository-owned delivery jobs `ui-web preview`, `ui-web preview cleanup`, and + `ui-web deployment`; keep them as delivery evidence rather than merge-required contexts. +- Cut over without downtime: keep Cloudflare automatic Git builds enabled while the runner delivery + foundation lands and proves one main push; prove runner preview on a subsequent non-stacked UI + pull request, then disable automatic preview and production builds, merge through the runner lane, + and verify the canonical site and close-time retirement. Previous deployments remain hosted + throughout the transition. The Cloudflare GitHub App is currently installed for all organization + repositories, so removing only `ui` access is not a Phase 5 exit condition; audit and narrow that + installation separately only when the other repositories' usage is known. +- Treat package publication and Histoire delivery as parallel outputs of protected `main`. A failure + in one lane does not prove the other artifact invalid; record and repair the failed authority + without republishing the successful output solely to synchronize run IDs. - Resolve current release pull request #34 before tightening protection: - - immediately approve its `action_required` run or manually dispatch `.github/workflows/ci.yml` against `changeset-release/main`; + - prefer approving its existing `action_required` pull-request run so the PR-only Changesets + validation executes as well as the workspace check; + - use manual dispatch against `changeset-release/main` only as a fallback, because the current + workflow skips Changesets validation for `workflow_dispatch`; pair that fallback with an + explicit exact-head release-diff/Changesets validation; - verify that the successful `Reproducible workspace check` is attached to the exact release pull-request head SHA and GitHub Actions app identity. - Accept manual approval or dispatch as the initial low-frequency release-PR operating procedure. - If repeated release-PR updates make that procedure materially costly, automate in a separately approved slice with a dedicated GitHub App installed only on `InKCre/ui`: @@ -165,9 +219,16 @@ GitHub uses defaults from a public `.github` repository only when a repository h - pass that token through `changesets/action`'s `with.github-token` input; - keep package publication on the built-in token; - do not use a classic PAT, execute pull-request head code under `pull_request_target`, or synthesize custom required checks. -- Extend the existing repository rulesets so `main` requires a pull request, conversation resolution, linear history, the current strict required context, and no bypass. Preserve deletion and non-fast-forward protection. +- After the Phase 5 workflow pull request reports successful `ui-web checks`, atomically replace the + old required context with `ui-web checks` bound to GitHub Actions app `15368`; do not add a second + compatibility job. In the same settings slice, extend the existing rulesets so `main` requires a + pull request, conversation resolution, linear history, the strict required context, and no bypass. + Preserve deletion and non-fast-forward protection. - Disable merge commits; retain squash and rebase. Changesets reads the final `.changeset` files, so either method preserves version intent. - In stacked package work, merge bottom-up and restack before checks. Never use `changeset-release/main` as the base of a feature stack. +- Require maintainer approval before workflows from every external fork contributor run. Approval + permits only secret-free `ui-web checks`; the same-repository identity gate independently denies + runner preview delivery to forks. ### `InKCre/docs` Documentation-Site Profile @@ -271,25 +332,73 @@ credential or a synthetic fork. ### Phase 4: Core Service Alignment -- Land contribution-link and immutable-Action-pin changes without changing the core runtime topology. -- Verify all four required contexts, preview identity and cleanup, GHCR publication, exact-current-main production guard, and Heroku health checks. -- Enable linear history and repository merge-method settings after checks are green. +- Do not use the intentionally unmergeable synchronized product PR #45 as the governance acceptance + vehicle. Create a dedicated clean branch/worktree from current `core-py/origin/main`, extract only + the contribution-link and immutable-Action-pin changes, and leave PR #45 draft and unchanged. +- Keep the repository check scoped to program code and infrastructure configuration. Configure Ruff + centrally to exclude Markdown so populated Hub references and task prose cannot fail the code gate; + retain actionlint and the existing Python, lock, migration, settings, type, and test contracts. +- Verify all four required contexts plus preview identity/delivery on the small governance pull + request. Do not copy PR #45's product-specific stale validation repairs into this slice. +- After those checks are green, enable linear history, disable merge commits while retaining squash + and rebase, and merge the governance pull request through the protected path. +- Use the resulting main runs to verify GHCR publication, exact-current-main production guard, + production health checks, and movement of `stable`; then verify preview cleanup and remove the + temporary worktree. - Rollback: revert documentation/pins through a pull request and restore merge settings. Do not roll back production artifacts solely to undo governance metadata. ### Phase 5: Package Repository Alignment -- Resolve `ui` release pull request #34 with exact-head evidence. -- Land release concurrency and any immutable-Action-pin changes. -- Exercise a disposable two-level stack bottom-up; verify restacking, latest-base checks, squash-default behavior, and optional rebase merge without merge commits. -- Extend the UI rulesets only after the release pull request can reliably obtain `Reproducible workspace check`. +- Refresh and export UI repository settings plus the current `inkcre-web-design` Cloudflare project + configuration. Confirm the existing organization Cloudflare credential can address that project; + if it cannot, stop before cutover and create a project-scoped Pages token rather than weakening the + workflow or replacing the live project. +- Amend organization governance with the ` ` check-context convention. Apply it only to + `ui` in this phase; do not reopen completed `client-web` or `core-py` workflow work merely to rename + contexts. +- Resolve and merge `ui` release pull request #34 under its existing required-context name, with + exact-head pull-request-event evidence, before changing that name; verify the resulting package + publication, tag, release, main Release run, Cloudflare main deployment, and canonical Histoire + site. If the old `action_required` run can no longer be approved, use exact-head manual CI plus a + separate release-diff/Changesets validation rather than silently omitting the PR-only step. +- Land the runner-CD foundation through the existing `Reproducible workspace check` context. Keep + every existing Action pin and pin each newly introduced artifact/deployment Action at its reviewed + immutable commit. Verify the existing `Cloudflare Pages` check and exact-head branch preview while + the pull integration remains active. After merge, verify the new runner-owned + `ui-web deployment` against `https://design.inkcre.dev` before disabling either Cloudflare build + lane. +- Use a second sequential, non-stacked governance/release pull request for the `ui-web checks` rename + and non-cancelling `ui-release-main` concurrency. Wait for that exact head to report the successful + new GitHub-Actions-owned context while the old rule still blocks merge. Prove runner-owned + `ui-web preview` and its deterministic `pr-N` alias, disable Cloudflare automatic Git deployments, + atomically switch the required context and remaining rules, then merge and verify runner-only + production delivery and close-time preview retirement before considering any organization-wide + Cloudflare App access change. +- Record manual exact-head dispatch or approval as the initial operating procedure for low-frequency + Changesets and design-token pull requests created by the built-in `GITHUB_TOKEN`. +- Do not create synthetic pull requests solely to exercise stacking. Keep the documented bottom-up + restack procedure available for real stacked work, and validate it only when such work naturally + occurs. +- Disable merge commits while retaining squash and rebase after the new required context is proven. - Treat dedicated GitHub App automation as a separate credential-bearing Impact Handshake after manual operating cost is observed. -- Rollback: restore rulesets and revert workflow changes. Revoke the optional App installation and secret if that later slice fails; its issued token expires independently. +- Rollback: before disabling automatic Cloudflare builds, retain the exported project/build settings. + If runner delivery fails before cutover, leave the pull integration active and revert the runner + workflow through a pull request. If it fails after cutover, re-enable automatic production and + preview builds first so `design.inkcre.dev` keeps receiving updates, then revert. Coordinate the + workflow job name and required-context rule so the ruleset never waits for a name the active + workflow cannot emit; restore the exported rulesets and merge settings as needed. Do not unpublish + an already successful package solely to roll back Histoire delivery. Revoke the optional release + App installation and secret if that later slice fails; its issued token expires independently. ### Phase 6: Actions Hardening and Final Audit -- Change per-repository workflow defaults to read-only and disallow Actions approval of pull-request reviews. -- Enforce immutable Action SHAs after the pin audit. -- Pilot and then apply selected-actions allowlists one repository at a time. +- Freeze each repository's Actions settings, fork policy, production branch policy, workflow action references, and rollback payload before mutation. +- Change remaining per-repository workflow defaults to read-only and disallow Actions approval of pull-request reviews. Preserve explicit job-level write grants for release and deployment jobs. +- Require maintainer approval before every external fork contributor's read-only, secret-free workflow run in all four repositories. +- Enforce immutable Action SHAs after confirming every checked-in external Action is already pinned to a full commit SHA; local actions remain local references. +- Add the missing `main`-only branch policy to the `client-web` production environment. +- Treat selected-actions allowlists as optional defense in depth. If justified, probe only `docs` first with GitHub-owned Actions plus exact current third-party action patterns, then restore `allowed_actions=all` on any unexplained workflow-start failure. Do not make the allowlist a Phase 6 exit condition. +- After each repository mutation, run a secret-free manual validation workflow and stop or restore that repository before continuing if it cannot start normally. - Run a final read-only audit against all common controls, profile-specific workflows, environment policies, templates, required-check sources, and merge methods. - Record any exception with owner, reason, compensating control, and review date in `GOVERNANCE.md`; do not hide drift in the task packet. - Exit condition: the final audit has no unexplained difference between the documented standard and live GitHub state. @@ -309,7 +418,6 @@ credential or a synthetic fork. - a failed latest-head required check blocks merge; - unresolved review conversation blocks merge; - an eligible corrected pull request merges through squash by default; - - a two-level stack can be merged bottom-up after restacking; - pull-request previews are isolated and cleaned without production authority; - protected-main release workflows build, identify, deploy or publish, and verify their own outputs; - direct administrator push is unavailable under normal configuration. diff --git a/tasks/organization-git-workflow/packet.md b/tasks/organization-git-workflow/packet.md index 7fd71b1..88e9b8d 100644 --- a/tasks/organization-git-workflow/packet.md +++ b/tasks/organization-git-workflow/packet.md @@ -3,8 +3,8 @@ - **Objective**: establish one organization-level Git and pull-request workflow for the active `core-py`, `client-web`, `ui`, and `docs` repositories, with a shared governance baseline and repository-type-specific CI/CD profiles. - **Guardrails**: stay within GitHub Free; keep application, package, and documentation-site delivery semantics distinct; use `InKCre/.github` as the future GitHub-native governance owner without moving product or system truth out of `InKCre/docs`; do not introduce cross-repository changelog or change-set machinery; keep pull-request validation free of canonical publication and production authority; do not change code, repository settings, workflows, or remote state without a separate explicit start. - **Verification**: live GitHub settings and checked-in workflows for all four repositories match the approved baseline; required checks protect pull requests before merge; unresolved review conversations block merge; pull-request workflows do not publish canonical artifacts or mutate shared release environments; any post-merge workflow has a repository-specific release or deployment purpose; checked source and released artifact identity are stated accurately; the organization default contribution guide and pull-request template render from `InKCre/.github`; a final read-only audit finds no unexplained governance drift. -- **Current Truth**: Phases 0-3 are complete. The Phase 3 acceptance anchor on `client-web/main` was `cc857104e07cc41bc77db19acf75676b0cb22d15`; later documentation-only merges may advance the branch without replacing that evidence. Pull-request and merge-group validation resolve core-py's production-admitted `stable` image once, generate Supabase types from its raw PostgreSQL schema artifact, run the real core service against fresh pgvector/PostgreSQL and PostgREST, and report the four direct required contexts `Workspace contract`, `Dependency review`, `client-web E2E`, and `client-webext E2E`. PR #49 and merge-group run `31256717813` live-proved all four checks on the synthetic current-main commit. Same-repository Pages preview, close-time stable-alias retirement, focused exact-main build, same-run artifact delivery, and production smoke are live-proven. Main protection now applies to administrators, requires resolved conversations and linear history, rejects force-push/deletion, and disables merge commits; Actions defaults are read-only, Actions cannot approve PRs, and every external fork run requires maintainer approval. The temporary one-entry merge queue was removed after the probe so squash remains the default rather than a forced merge method and rebase remains available. Core producer PRs #43/#44 and client PRs #40/#43/#45/#47/#48/#49 merged normally without administrator bypass. Deferred synchronized product work is preserved as mergeable draft PRs [client-web #50](https://github.com/InKCre/client-web/pull/50) and [core-py #45](https://github.com/InKCre/core-py/pull/45), with original snapshot branches retained. -- **Next Step**: The matching `.github` governance amendment is published as `8018be3`. Proceed only under a separate start to the remaining `core-py`, `ui`, and organization-wide hardening phases. Review core-py #45 first; when its schema-bearing service reaches `stable`, rerun client-web #50 so generated types and real-service E2E become its admission proof. Keep PR #50 draft until then. Do not weaken E2E or restore the obsolete checked source/image/migration tuple. +- **Current Truth**: Phases 0-5 are complete and Phase 6 has started. The Phase 3 acceptance anchor on `client-web/main` was `cc857104e07cc41bc77db19acf75676b0cb22d15`; later documentation-only merges may advance the branch without replacing that evidence. Pull-request and merge-group validation resolve core-py's production-admitted `stable` image once, generate Supabase types from its raw PostgreSQL schema artifact, run the real core service against fresh pgvector/PostgreSQL and PostgREST, and report the four direct required contexts `Workspace contract`, `Dependency review`, `client-web E2E`, and `client-webext E2E`. PR #49 and merge-group run `31256717813` live-proved all four checks on the synthetic current-main commit. Same-repository Pages preview, close-time stable-alias retirement, focused exact-main build, same-run artifact delivery, and production smoke are live-proven. Client-web main protection applies to administrators, requires resolved conversations and linear history, rejects force-push/deletion, and disables merge commits; its Actions defaults are read-only, Actions cannot approve PRs, and every external fork run requires maintainer approval. The temporary one-entry merge queue was removed after the probe so squash remains the default rather than a forced merge method and rebase remains available. Core producer PRs #43/#44 and client PRs #40/#43/#45/#47/#48/#49 merged normally without administrator bypass. Deferred synchronized product work is preserved as mergeable draft PRs [client-web #50](https://github.com/InKCre/client-web/pull/50) and [core-py #45](https://github.com/InKCre/core-py/pull/45), with original snapshot branches retained. Core-py PR #45 was intentionally excluded from Phase 4 because its first product commit already changes the database contract. Independent [core-py PR #46](https://github.com/InKCre/core-py/pull/46) was squash-merged through the protected path as main `531b0d28`; it links organization governance, pins external Actions, and excludes Markdown from Ruff's code gate without changing runtime or database topology. PR required checks and preview were green in `31261340753`, `31261340743`, and `31261340759`; main CI `31261689850`, immutable publication `31261772024`, production `31261814390`, and close cleanup `31261690128`/`31261689954` all succeeded. GHCR digest `sha256:6c5899817a5eaaa899ea4070bde304ff8ebb46089e2f63930be08c199a468f5f` carries the exact main SHA, `main`, and `stable` tags after production admission. Core-py now requires linear history, disables merge commits, retains squash/rebase, and preserves all four strict GitHub-Actions-bound checks plus admin/conversation/no-force/no-delete controls. UI Phase 5 preserved Changesets package publication, moved Histoire preview and production delivery to repository-owned runner push, adopted `ui-web checks`, serialized release runs, disabled Cloudflare automatic Git builds, and enforced the same PR/conversation/linear/no-bypass merge baseline through repository rulesets. +- **Next Step**: Publish the final Phase 6 records after explicit commit and push authorization. All four repositories now use read-only default workflow tokens, prohibit Actions review approval, require maintainer approval for every external fork contributor, enforce full Action SHAs, and retain `allowed_actions=all`; `client-web` production now accepts only `main`. Secret-free validation runs `31300335436`, `31300376700`, `31300460833`, and `31300570405` passed for docs, UI, core-py, and client-web respectively. The final remote audit found no unpinned external Action, required-check source mismatch, main-protection drift, merge-method drift, or production branch-policy drift. The local organization governance update and task evidence are not yet committed or pushed. Selected-actions allowlists remain optional and were not applied. Separately, when core-py PR #45's schema-bearing service is ready and reaches `stable`, rerun client-web #50; keep it draft until then. ## Target `client-web` Operating Model @@ -89,7 +89,7 @@ leaving peer-dependent application code on main would remain a false green and i - `client-web` PR #29 checked synthetic merge commit `9cb69ce`; its tree `7bc0d1a` equals the final squash commit `400c581` tree, while the commit identities differ. The main run rebuilds that tree and `pages-deploy.yml` deploys the uploaded `client-web-dist` unchanged. - `core-py` CI builds and tests the Dockerfile's `artifact` target; `artifact-publish.yml` rebuilds that target for GHCR; `production-deploy.yml` separately builds `heroku-web`, `heroku-release`, and PostgREST images before delivery. - `docs` `Website checks` builds and uploads `inkcre-website-dist` on pull requests and main; `Pages deployment` downloads the successful current-main artifact without rebuilding. Current main run `31143401361` and deployment `31143435829` are green, but `Website contract` is not required because main is unprotected. - - `ui` requires `Reproducible workspace check` through a repository ruleset. Its main-only Changesets workflow creates a release PR when changesets exist and publishes `@inkcre/ui-web` after that PR reaches main. Release PR #34 is currently blocked because its bot-authored check run is `action_required` and the required context was not emitted. + - `ui` requires strict GitHub-Actions-owned `ui-web checks` through repository ruleset `19865127`. Its main-only Changesets workflow creates a release PR when changesets exist and publishes `@inkcre/ui-web` after that PR reaches main. Release runs are serialized without cancellation. The exact-head run for release PR #34 was manually approved, the check passed, and the merged release published `@inkcre/ui-web@1.3.1`; dedicated release-App automation remains deferred until repeated manual cost is demonstrated. - Decisions: - Scope is the four active repositories; `InKCre/.github` is the governance carrier and control-plane exception; GitHub Team is out of scope; cross-repository change-set and changelog coordination are out of scope. - Pull-request workflows validate candidate changes. They may use scoped delivery capability to create an isolated, deterministic, short-lived preview as validation evidence, but may not publish a canonical release, mutate a shared release environment, or exercise production authority. Preview writes still require a trusted controller, same-repository source, scoped credentials, deterministic naming, concurrency isolation, and cleanup. A persistent shared staging environment is part of the release lane rather than this preview exception. @@ -116,6 +116,8 @@ leaving peer-dependent application code on main would remain a false green and i - `docs` pull-request validation includes an isolated Cloudflare Pages preview for successful same-repository pull requests. The preview uses the checked pull-request artifact through a trusted controller, deterministic PR namespace, `noindex`, concurrency isolation, and close-time retirement; it never becomes the production artifact. Closure replaces the live preview with a trusted `noindex` tombstone; older immutable deployments remain in Cloudflare history. Fork pull requests remain build/test only. - Unresolved review conversations should block merge. Merge commits should be disabled and linear history required. Squash is the documented default; rebase merge remains available for intentionally curated commit series and stacked-pull-request work. GitHub exposes allowed merge methods but no repository setting that makes squash the enforced UI default while rebase remains available, so the preference is a documented convention rather than a mechanical gate. - The initial rollout requires pull requests but zero approving reviews; conversation resolution and required checks remain mandatory. Automatic branch deletion stays disabled while stacked pull requests are being learned. + - Public GitHub check names use lowercase ` `. `scope` is the repository or an independently understood application/package inside a monorepo. Validation kinds are `checks`, `tests`, and `e2e tests`; delivery jobs use `preview`, `preview cleanup`, and `deployment`. Public names identify ownership and evidence class, while detailed implementation terms remain step-level truth. Phase 5 adopts `ui-web checks` and the UI delivery names; existing client, extension, core, and docs contexts remain unchanged until a future justified workflow change. + - `ui` has deployment as well as package publication. Phase 5 reused the existing `inkcre-web-design` project and custom domain while moving Histoire build and delivery from Cloudflare's Git pull lane to runner-pushed, pinned-Wrangler workflows and checked-in Pages configuration. Secret-free `ui-web checks` supplies the exact preview artifact; a trusted same-repository controller deploys deterministic `pr-N`, close replaces that alias with a `noindex` tombstone, and protected main builds and pushes its own same-run artifact before canonical smoke. A dual-run main proof preceded the cutover; Cloudflare automatic preview and production builds are now disabled. The Cloudflare GitHub App remains connected with all-repository organization access, so narrowing that installation is deferred until other consumers are audited rather than risk changing unrelated repositories in Phase 5. Package release and Histoire delivery remain independent protected-main outputs; neither lane's success proves the other succeeded, and historical immutable previews remain reachable by Cloudflare design. - `.github` is not an active product repository and is outside the common branch-policy baseline. Phase 1 directly published its GitHub-native governance/community files to `main`, retired the superseded VitePress/Pages site, and moved the organization website link to `https://inkcre.dev`. - Open questions: whether the private core image is readable in an approved secret-free fork workflow or should become public; whether a future typed consumer actually needs a numeric pgvector override; how Heroku's registry reports the transferred image ID relative to the GHCR manifest; the final squash-commit title/body defaults; whether repeated `ui` release-PR approvals justify a separate repository-scoped GitHub App; and whether real stack frequency justifies stack tooling. - Work: [`implementation-plan.md`](./implementation-plan.md) contains the independent, phased mutation and verification plan. From bd08c2df616b2987c5f8c92c80f817905e444b6f Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Sun, 9 Aug 2026 15:24:49 +0800 Subject: [PATCH 2/7] docs(governance): record publication state - mark Phase 6 complete after live settings verification - link organization commit and client-web evidence PR --- .../organization-git-workflow/evidence/phase-6/README.md | 9 +++++---- tasks/organization-git-workflow/implementation-plan.md | 2 +- tasks/organization-git-workflow/packet.md | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tasks/organization-git-workflow/evidence/phase-6/README.md b/tasks/organization-git-workflow/evidence/phase-6/README.md index 9595bb0..1f94a77 100644 --- a/tasks/organization-git-workflow/evidence/phase-6/README.md +++ b/tasks/organization-git-workflow/evidence/phase-6/README.md @@ -53,11 +53,12 @@ change product code or repository-specific CI/CD topology. its implementation-specific contribution guide, client-web owns its application-specific pull- request template, and the organization defaults cover missing files. -## Remaining publication +## Publication -- The local `InKCre/.github/GOVERNANCE.md` change states the all-external fork approval rule and the - actual environment-job credential boundary. It requires explicit commit and push authorization. -- The task packet and this evidence remain local until explicitly authorized for commit and push. +- `InKCre/.github` commit `510cdd3` publishes the all-external fork approval rule and the actual + environment-job credential boundary directly on its non-product governance `main`. +- Documentation-only client-web PR #53 records the completed task state and this evidence without + changing product code, workflows, repository settings, or delivery behavior. ## Rollback diff --git a/tasks/organization-git-workflow/implementation-plan.md b/tasks/organization-git-workflow/implementation-plan.md index 671d669..93ad35d 100644 --- a/tasks/organization-git-workflow/implementation-plan.md +++ b/tasks/organization-git-workflow/implementation-plan.md @@ -1,6 +1,6 @@ # Organization Git and GitHub Workflow Implementation Plan -- **Status**: Phases 0-5 are complete. Phase 3 landed the core schema-bearing stable release foundation, the client stable resolver/typegen/real-service E2E contract, separate PR preview and exact-main delivery controllers, direct required-check names, and the accepted repository settings. Client PR #49 was admitted through a real one-entry merge queue; merge-group run `31256717813` passed all four required checks on synthetic commit `cc857104`, which became main. The temporary queue rule was then removed so GitHub does not force one merge method; squash remains the default and rebase remains available. Preview cleanup run `31256442627`, automatic cleanup run `31256837743`, and production runs `31256434332`/`31256836894` passed. Deferred full-stack preview bootstrap and live external-fork package-access probing are recorded rather than represented as finished. The reverted synchronized product series is preserved without conflicts in draft client-web #50 and core-py #45. Phase 4 independently aligned core-py governance through merged PR #46 and live-proved main CI, immutable publication, production admission, `stable`, and cleanup before removing its temporary worktree. Immediate fan-out, dependency-update pull requests, and a cross-repository GitHub App remain rejected. Phase 5 resolved release PR #34 and package `1.3.1`, published the check-name governance, merged runner-CD foundation PR #35 and sequential cutover PR #36, disabled Cloudflare automatic Git builds after dual-run proof, live-proved runner preview/cleanup and exact-main same-run production delivery, adopted `ui-web checks`, serialized release runs, and completed the UI ruleset/merge-method transition. Phase 6 has applied and live-verified the four repositories' read-only workflow defaults, disabled Actions review approval, all-external fork approval, full-SHA enforcement, and the missing client-web production-main policy. Final settings and workflow audits pass; only explicit publication of the local governance and task-packet records remains. +- **Status**: Phases 0-6 are complete. Phase 3 landed the core schema-bearing stable release foundation, the client stable resolver/typegen/real-service E2E contract, separate PR preview and exact-main delivery controllers, direct required-check names, and the accepted repository settings. Client PR #49 was admitted through a real one-entry merge queue; merge-group run `31256717813` passed all four required checks on synthetic commit `cc857104`, which became main. The temporary queue rule was then removed so GitHub does not force one merge method; squash remains the default and rebase remains available. Preview cleanup run `31256442627`, automatic cleanup run `31256837743`, and production runs `31256434332`/`31256836894` passed. Deferred full-stack preview bootstrap and live external-fork package-access probing are recorded rather than represented as finished. The reverted synchronized product series is preserved without conflicts in draft client-web #50 and core-py #45. Phase 4 independently aligned core-py governance through merged PR #46 and live-proved main CI, immutable publication, production admission, `stable`, and cleanup before removing its temporary worktree. Immediate fan-out, dependency-update pull requests, and a cross-repository GitHub App remain rejected. Phase 5 resolved release PR #34 and package `1.3.1`, published the check-name governance, merged runner-CD foundation PR #35 and sequential cutover PR #36, disabled Cloudflare automatic Git builds after dual-run proof, live-proved runner preview/cleanup and exact-main same-run production delivery, adopted `ui-web checks`, serialized release runs, and completed the UI ruleset/merge-method transition. Phase 6 applied and live-verified the four repositories' read-only workflow defaults, disabled Actions review approval, all-external fork approval, full-SHA enforcement, and the missing client-web production-main policy. Final settings and workflow audits pass; `.github` commit `510cdd3` publishes the final guardrail and client-web PR #53 records the completed task evidence. - **Control surface**: [`packet.md`](./packet.md) owns current task state and decisions. This file owns execution order, planned mutations, verification, and rollback. - **Durable owner**: `InKCre/.github` will own the organization Git and GitHub workflow standard. Repository code, tests, release mechanics, and profile-specific instructions remain with each repository. - **Enforcement scope**: `InKCre/core-py`, `InKCre/client-web`, `InKCre/ui`, and `InKCre/docs`. `InKCre/.github` is included only as the governance control plane. diff --git a/tasks/organization-git-workflow/packet.md b/tasks/organization-git-workflow/packet.md index 88e9b8d..5487142 100644 --- a/tasks/organization-git-workflow/packet.md +++ b/tasks/organization-git-workflow/packet.md @@ -3,8 +3,8 @@ - **Objective**: establish one organization-level Git and pull-request workflow for the active `core-py`, `client-web`, `ui`, and `docs` repositories, with a shared governance baseline and repository-type-specific CI/CD profiles. - **Guardrails**: stay within GitHub Free; keep application, package, and documentation-site delivery semantics distinct; use `InKCre/.github` as the future GitHub-native governance owner without moving product or system truth out of `InKCre/docs`; do not introduce cross-repository changelog or change-set machinery; keep pull-request validation free of canonical publication and production authority; do not change code, repository settings, workflows, or remote state without a separate explicit start. - **Verification**: live GitHub settings and checked-in workflows for all four repositories match the approved baseline; required checks protect pull requests before merge; unresolved review conversations block merge; pull-request workflows do not publish canonical artifacts or mutate shared release environments; any post-merge workflow has a repository-specific release or deployment purpose; checked source and released artifact identity are stated accurately; the organization default contribution guide and pull-request template render from `InKCre/.github`; a final read-only audit finds no unexplained governance drift. -- **Current Truth**: Phases 0-5 are complete and Phase 6 has started. The Phase 3 acceptance anchor on `client-web/main` was `cc857104e07cc41bc77db19acf75676b0cb22d15`; later documentation-only merges may advance the branch without replacing that evidence. Pull-request and merge-group validation resolve core-py's production-admitted `stable` image once, generate Supabase types from its raw PostgreSQL schema artifact, run the real core service against fresh pgvector/PostgreSQL and PostgREST, and report the four direct required contexts `Workspace contract`, `Dependency review`, `client-web E2E`, and `client-webext E2E`. PR #49 and merge-group run `31256717813` live-proved all four checks on the synthetic current-main commit. Same-repository Pages preview, close-time stable-alias retirement, focused exact-main build, same-run artifact delivery, and production smoke are live-proven. Client-web main protection applies to administrators, requires resolved conversations and linear history, rejects force-push/deletion, and disables merge commits; its Actions defaults are read-only, Actions cannot approve PRs, and every external fork run requires maintainer approval. The temporary one-entry merge queue was removed after the probe so squash remains the default rather than a forced merge method and rebase remains available. Core producer PRs #43/#44 and client PRs #40/#43/#45/#47/#48/#49 merged normally without administrator bypass. Deferred synchronized product work is preserved as mergeable draft PRs [client-web #50](https://github.com/InKCre/client-web/pull/50) and [core-py #45](https://github.com/InKCre/core-py/pull/45), with original snapshot branches retained. Core-py PR #45 was intentionally excluded from Phase 4 because its first product commit already changes the database contract. Independent [core-py PR #46](https://github.com/InKCre/core-py/pull/46) was squash-merged through the protected path as main `531b0d28`; it links organization governance, pins external Actions, and excludes Markdown from Ruff's code gate without changing runtime or database topology. PR required checks and preview were green in `31261340753`, `31261340743`, and `31261340759`; main CI `31261689850`, immutable publication `31261772024`, production `31261814390`, and close cleanup `31261690128`/`31261689954` all succeeded. GHCR digest `sha256:6c5899817a5eaaa899ea4070bde304ff8ebb46089e2f63930be08c199a468f5f` carries the exact main SHA, `main`, and `stable` tags after production admission. Core-py now requires linear history, disables merge commits, retains squash/rebase, and preserves all four strict GitHub-Actions-bound checks plus admin/conversation/no-force/no-delete controls. UI Phase 5 preserved Changesets package publication, moved Histoire preview and production delivery to repository-owned runner push, adopted `ui-web checks`, serialized release runs, disabled Cloudflare automatic Git builds, and enforced the same PR/conversation/linear/no-bypass merge baseline through repository rulesets. -- **Next Step**: Publish the final Phase 6 records after explicit commit and push authorization. All four repositories now use read-only default workflow tokens, prohibit Actions review approval, require maintainer approval for every external fork contributor, enforce full Action SHAs, and retain `allowed_actions=all`; `client-web` production now accepts only `main`. Secret-free validation runs `31300335436`, `31300376700`, `31300460833`, and `31300570405` passed for docs, UI, core-py, and client-web respectively. The final remote audit found no unpinned external Action, required-check source mismatch, main-protection drift, merge-method drift, or production branch-policy drift. The local organization governance update and task evidence are not yet committed or pushed. Selected-actions allowlists remain optional and were not applied. Separately, when core-py PR #45's schema-bearing service is ready and reaches `stable`, rerun client-web #50; keep it draft until then. +- **Current Truth**: Phases 0-6 are complete. The Phase 3 acceptance anchor on `client-web/main` was `cc857104e07cc41bc77db19acf75676b0cb22d15`; later documentation-only merges may advance the branch without replacing that evidence. Pull-request and merge-group validation resolve core-py's production-admitted `stable` image once, generate Supabase types from its raw PostgreSQL schema artifact, run the real core service against fresh pgvector/PostgreSQL and PostgREST, and report the four direct required contexts `Workspace contract`, `Dependency review`, `client-web E2E`, and `client-webext E2E`. PR #49 and merge-group run `31256717813` live-proved all four checks on the synthetic current-main commit. Same-repository Pages preview, close-time stable-alias retirement, focused exact-main build, same-run artifact delivery, and production smoke are live-proven. Client-web main protection applies to administrators, requires resolved conversations and linear history, rejects force-push/deletion, and disables merge commits; its Actions defaults are read-only, Actions cannot approve PRs, and every external fork run requires maintainer approval. The temporary one-entry merge queue was removed after the probe so squash remains the default rather than a forced merge method and rebase remains available. Core producer PRs #43/#44 and client PRs #40/#43/#45/#47/#48/#49 merged normally without administrator bypass. Deferred synchronized product work is preserved as mergeable draft PRs [client-web #50](https://github.com/InKCre/client-web/pull/50) and [core-py #45](https://github.com/InKCre/core-py/pull/45), with original snapshot branches retained. Core-py PR #45 was intentionally excluded from Phase 4 because its first product commit already changes the database contract. Independent [core-py PR #46](https://github.com/InKCre/core-py/pull/46) was squash-merged through the protected path as main `531b0d28`; it links organization governance, pins external Actions, and excludes Markdown from Ruff's code gate without changing runtime or database topology. PR required checks and preview were green in `31261340753`, `31261340743`, and `31261340759`; main CI `31261689850`, immutable publication `31261772024`, production `31261814390`, and close cleanup `31261690128`/`31261689954` all succeeded. GHCR digest `sha256:6c5899817a5eaaa899ea4070bde304ff8ebb46089e2f63930be08c199a468f5f` carries the exact main SHA, `main`, and `stable` tags after production admission. Core-py now requires linear history, disables merge commits, retains squash/rebase, and preserves all four strict GitHub-Actions-bound checks plus admin/conversation/no-force/no-delete controls. UI Phase 5 preserved Changesets package publication, moved Histoire preview and production delivery to repository-owned runner push, adopted `ui-web checks`, serialized release runs, disabled Cloudflare automatic Git builds, and enforced the same PR/conversation/linear/no-bypass merge baseline through repository rulesets. Phase 6 aligned repository-level Actions controls, added the missing client-web production policy, passed four live validation runs, published the final organization guardrail as `.github` commit `510cdd3`, and found no unexplained governance drift. +- **Next Step**: Review and merge documentation-only [client-web PR #53](https://github.com/InKCre/client-web/pull/53), which records the already-complete governance rollout; it does not gate the live settings. Selected-actions allowlists remain optional and were not applied. Separately, when core-py PR #45's schema-bearing service is ready and reaches `stable`, rerun client-web #50; keep it draft until then. ## Target `client-web` Operating Model From 036e0de8cf543ac018be108a9c025e079cdd97c0 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Sun, 2 Aug 2026 23:54:08 +0800 Subject: [PATCH 3/7] feat(core): support semantic content and binary storage - add peer-local block hydration and PostgreSQL byte CRUD - register exact semantic resolvers with safe browser handles and typed outcomes - migrate producers and document the peer info-base architecture --- .../info-base/BlockNode/BlockNode.vue | 6 +- .../block/BlockContent/BlockContent.vue | 40 +- .../info-base/resolvers/ContentAudio.vue | 23 + .../info-base/resolvers/ContentFile.vue | 34 ++ .../info-base/resolvers/ContentHtml.vue | 34 +- .../info-base/resolvers/ContentImage.vue | 6 +- .../info-base/resolvers/ContentVideo.vue | 83 +--- apps/client-web/src/core.ts | 14 + apps/client-web/src/storages/index.ts | 10 +- .../src/views/info-base/graph/graph.vue | 8 +- .../info-base/ArcsEditor/ArcsEditor.vue | 2 +- .../taking-note.sidepanel/TakingNote.vue | 6 +- docs/info-base/ARCHITECTURE.md | 428 ++++++------------ docs/resolver.md | 12 +- .../components/contentTweet/contentTweet.vue | 2 +- extensions/twitter/src/resolver.ts | 29 +- extensions/twitter/src/schema.ts | 5 +- packages/core/src/base/db-api.ts | 49 ++ packages/core/src/index.ts | 36 +- packages/core/src/info-base/block.test.ts | 53 +++ packages/core/src/info-base/block.ts | 44 ++ packages/core/src/info-base/index.ts | 35 +- .../src/info-base/resolvers/actual-content.ts | 29 ++ .../core/src/info-base/resolvers/audio.ts | 42 ++ packages/core/src/info-base/resolvers/base.ts | 111 +++-- .../core/src/info-base/resolvers/cache.ts | 3 + .../src/info-base/resolvers/contracts.test.ts | 83 ++++ .../core/src/info-base/resolvers/contracts.ts | 60 +++ .../core/src/info-base/resolvers/decode.ts | 43 ++ packages/core/src/info-base/resolvers/epub.ts | 46 ++ packages/core/src/info-base/resolvers/file.ts | 26 ++ packages/core/src/info-base/resolvers/html.ts | 31 +- .../core/src/info-base/resolvers/image.ts | 59 ++- .../core/src/info-base/resolvers/index.ts | 50 +- packages/core/src/info-base/resolvers/pdf.ts | 40 ++ .../resolvers/semantic-content.test.ts | 81 ++++ packages/core/src/info-base/resolvers/text.ts | 18 +- .../core/src/info-base/resolvers/video.ts | 54 ++- packages/core/src/info-base/resolvers/zip.ts | 40 ++ packages/core/src/info-base/storages/base.ts | 35 +- .../core/src/info-base/storages/http.test.ts | 41 ++ packages/core/src/info-base/storages/http.ts | 278 +++--------- packages/core/src/info-base/storages/index.ts | 17 +- .../src/info-base/storages/postgresql.test.ts | 71 +++ .../core/src/info-base/storages/postgresql.ts | 78 ++++ 45 files changed, 1453 insertions(+), 842 deletions(-) create mode 100644 apps/client-web/src/components/info-base/resolvers/ContentAudio.vue create mode 100644 apps/client-web/src/components/info-base/resolvers/ContentFile.vue create mode 100644 packages/core/src/info-base/block.test.ts create mode 100644 packages/core/src/info-base/resolvers/actual-content.ts create mode 100644 packages/core/src/info-base/resolvers/audio.ts create mode 100644 packages/core/src/info-base/resolvers/contracts.test.ts create mode 100644 packages/core/src/info-base/resolvers/contracts.ts create mode 100644 packages/core/src/info-base/resolvers/decode.ts create mode 100644 packages/core/src/info-base/resolvers/epub.ts create mode 100644 packages/core/src/info-base/resolvers/file.ts create mode 100644 packages/core/src/info-base/resolvers/pdf.ts create mode 100644 packages/core/src/info-base/resolvers/semantic-content.test.ts create mode 100644 packages/core/src/info-base/resolvers/zip.ts create mode 100644 packages/core/src/info-base/storages/http.test.ts create mode 100644 packages/core/src/info-base/storages/postgresql.test.ts create mode 100644 packages/core/src/info-base/storages/postgresql.ts diff --git a/apps/client-web/src/components/info-base/BlockNode/BlockNode.vue b/apps/client-web/src/components/info-base/BlockNode/BlockNode.vue index 45e4830..d53c406 100644 --- a/apps/client-web/src/components/info-base/BlockNode/BlockNode.vue +++ b/apps/client-web/src/components/info-base/BlockNode/BlockNode.vue @@ -2,7 +2,6 @@ import { Handle, Position } from '@vue-flow/core' import type { BlockNodeProps } from './BlockNode' import { blockNodeEmits } from './BlockNode' -import BlockContent from '../block/BlockContent/BlockContent.vue' const props = defineProps() const emit = defineEmits(blockNodeEmits) @@ -20,7 +19,10 @@ const onNodeClick = () => {
- +
+ {{ props.data.block.resolver }} + {{ props.data.preview }} +
diff --git a/apps/client-web/src/components/info-base/block/BlockContent/BlockContent.vue b/apps/client-web/src/components/info-base/block/BlockContent/BlockContent.vue index 47e1a1c..08dd592 100644 --- a/apps/client-web/src/components/info-base/block/BlockContent/BlockContent.vue +++ b/apps/client-web/src/components/info-base/block/BlockContent/BlockContent.vue @@ -1,7 +1,7 @@ @@ -45,21 +58,20 @@ onUnmounted(async () => {
! - {{ state.error?.message || 'Failed to load content' }} + {{ setupError?.message || state.error?.message || 'Failed to load content' }}
-
- {{ block.content }} -
+
Content unavailable
diff --git a/apps/client-web/src/components/info-base/resolvers/ContentAudio.vue b/apps/client-web/src/components/info-base/resolvers/ContentAudio.vue new file mode 100644 index 0000000..ac662ed --- /dev/null +++ b/apps/client-web/src/components/info-base/resolvers/ContentAudio.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/apps/client-web/src/components/info-base/resolvers/ContentFile.vue b/apps/client-web/src/components/info-base/resolvers/ContentFile.vue new file mode 100644 index 0000000..798516d --- /dev/null +++ b/apps/client-web/src/components/info-base/resolvers/ContentFile.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/apps/client-web/src/components/info-base/resolvers/ContentHtml.vue b/apps/client-web/src/components/info-base/resolvers/ContentHtml.vue index 3d4d978..28c63db 100644 --- a/apps/client-web/src/components/info-base/resolvers/ContentHtml.vue +++ b/apps/client-web/src/components/info-base/resolvers/ContentHtml.vue @@ -1,35 +1,14 @@ diff --git a/apps/client-web/src/core.ts b/apps/client-web/src/core.ts index 47f1855..e94adf0 100644 --- a/apps/client-web/src/core.ts +++ b/apps/client-web/src/core.ts @@ -9,10 +9,16 @@ import { configStore, localStorageAdapter, setMFImplementation, + registerCoreResolvers, TextResolver, + AudioResolver, + EpubResolver, + FileResolver, ImageResolver, + PdfResolver, VideoResolver, HtmlResolver, + ZipResolver, } from '@inkcre/core' import { createInstance } from '@module-federation/enhanced/runtime' import * as InKCreCore from '@inkcre/core' @@ -26,6 +32,8 @@ import ContentText from '@/components/info-base/resolvers/ContentText.vue' import ContentImage from '@/components/info-base/resolvers/ContentImage.vue' import ContentVideo from '@/components/info-base/resolvers/ContentVideo.vue' import ContentHtml from '@/components/info-base/resolvers/ContentHtml.vue' +import ContentAudio from '@/components/info-base/resolvers/ContentAudio.vue' +import ContentFile from '@/components/info-base/resolvers/ContentFile.vue' // ============================================================================ // Resolver Component Registration @@ -37,9 +45,15 @@ import ContentHtml from '@/components/info-base/resolvers/ContentHtml.vue' */ export function setupResolvers(): void { TextResolver.contentComp = ContentText + AudioResolver.contentComp = ContentAudio + EpubResolver.contentComp = ContentFile + FileResolver.contentComp = ContentFile ImageResolver.contentComp = ContentImage + PdfResolver.contentComp = ContentFile VideoResolver.contentComp = ContentVideo HtmlResolver.contentComp = ContentHtml + ZipResolver.contentComp = ContentFile + registerCoreResolvers() console.log('[Core] Resolver components registered') } diff --git a/apps/client-web/src/storages/index.ts b/apps/client-web/src/storages/index.ts index 1e0e04b..9e6434d 100644 --- a/apps/client-web/src/storages/index.ts +++ b/apps/client-web/src/storages/index.ts @@ -6,14 +6,8 @@ */ // Import all storage handlers to trigger @Storage.registry decorator -import { - HttpImageStorage, - HttpVideoStorage, - HttpTextStorage, - HttpHtmlStorage, - HttpJsonStorage, -} from '@inkcre/core' +import { HttpStorage, PostgreSQLBinaryStorage } from '@inkcre/core' // Handlers are auto-registered via decorator, no need to do anything else // Just ensure they're imported -export { HttpImageStorage, HttpVideoStorage, HttpTextStorage, HttpHtmlStorage, HttpJsonStorage } +export { HttpStorage, PostgreSQLBinaryStorage } diff --git a/apps/client-web/src/views/info-base/graph/graph.vue b/apps/client-web/src/views/info-base/graph/graph.vue index 9724097..8b3366a 100644 --- a/apps/client-web/src/views/info-base/graph/graph.vue +++ b/apps/client-web/src/views/info-base/graph/graph.vue @@ -149,8 +149,12 @@ const loadData = async () => { // Transform blocks to nodes with relations allNodes.value = blocks.map((block) => { const blockRelations = blockRelationsMap.get(block.id) ?? [] - // Use block.content as preview (truncated) - const preview = block.content.length > 50 ? block.content.slice(0, 50) + '...' : block.content + const preview = + block.storage === null + ? block.content.length > 50 + ? block.content.slice(0, 50) + '...' + : block.content + : 'Stored content — select to open' return blockToNode(block, preview, blockRelations) }) diff --git a/apps/client-webext/components/info-base/ArcsEditor/ArcsEditor.vue b/apps/client-webext/components/info-base/ArcsEditor/ArcsEditor.vue index 30c895a..695a199 100644 --- a/apps/client-webext/components/info-base/ArcsEditor/ArcsEditor.vue +++ b/apps/client-webext/components/info-base/ArcsEditor/ArcsEditor.vue @@ -24,7 +24,7 @@ function addArc() { created_at: undefined, updated_at: undefined, storage: null, - resolver: 'text', + resolver: 'core.text.v1', content: '', }) const newArc = new ArcForm( diff --git a/apps/client-webext/entrypoints/taking-note.sidepanel/TakingNote.vue b/apps/client-webext/entrypoints/taking-note.sidepanel/TakingNote.vue index 4b5ce67..484051c 100644 --- a/apps/client-webext/entrypoints/taking-note.sidepanel/TakingNote.vue +++ b/apps/client-webext/entrypoints/taking-note.sidepanel/TakingNote.vue @@ -19,7 +19,7 @@ const form = ref( created_at: undefined, updated_at: undefined, storage: null, - resolver: 'text', + resolver: 'core.text.v1', content: '', }) ) @@ -64,8 +64,8 @@ watch( new BlockForm({ created_at: undefined, updated_at: undefined, - storage: null, - resolver: 'url', + storage: -1, + resolver: 'core.html.v1', content: newUrl, }) ) diff --git a/docs/info-base/ARCHITECTURE.md b/docs/info-base/ARCHITECTURE.md index 9bda8b0..0028956 100644 --- a/docs/info-base/ARCHITECTURE.md +++ b/docs/info-base/ARCHITECTURE.md @@ -1,354 +1,178 @@ -# Info-Base Architecture - -## Table of Contents - -- Overview -- Block Architecture -- Relation Architecture -- Storage System -- Resolver System -- Graph Operations -- Layout System -- Extension Points - ---- - -## Overview - -The **info-base** is InKCre's knowledge graph subsystem, providing a flexible graph-based information model with pluggable content types and storage backends. - -### Core Concepts - -``` -┌──────────────────────────────────────────────┐ -│ Knowledge Graph │ -│ │ -│ ┌───────┐ ┌───────┐ │ -│ │ Block │────────→│ Block │ │ -│ │ (Node)│ │ (Node)│ │ -│ └───┬───┘ └───────┘ │ -│ │ │ -│ │ Relation (Edge) │ -│ ↓ │ -│ ┌───────┐ │ -│ │ Block │ │ -│ └───────┘ │ -└──────────────────────────────────────────────┘ - -Block: Information unit (node) -Relation: Connection between blocks (edge) -Storage: Where block content is stored -Resolver: How block content is displayed -``` - -### Key Features - -- **Graph-based**: Blocks as nodes, relations as edges -- **Typed Content**: Resolver system for different content types -- **Pluggable Storage**: Abstract storage layer (URL, Blob, Text, etc.) -- **Lazy Loading**: On-demand content and relation fetching -- **Extensible**: Extensions can add custom resolvers/storages -- **Visualization**: Vue Flow integration with multiple layouts - -### Star Graph Pattern - -Each block can be resolved with its **star graph** - the block plus all directly connected relations: - -``` - ┌─────────┐ - │ Block A │ - └────┬────┘ - │ - ┌───────┼───────┐ - │ │ │ -┌───▼───┐ ┌─▼─────┐ ┌▼───────┐ -│Block B│ │Block C│ │Block D │ -└───────┘ └───────┘ └────────┘ - -Star Graph = {Block A, Relations to B/C/D} -``` - ---- - -## Block Architecture - -### Data Model - -Blocks represent information units with the following key fields: - -| Field | Type | Purpose | -| ------------ | -------------- | ----------------------------------------------------------- | -| `id` | number | Unique identifier | -| `created_at` | Date | Creation timestamp | -| `updated_at` | Date | Last modification timestamp | -| `storage` | number \| null | FK to Storage table (null = inline content) | -| `resolver` | string | Resolver type identifier (e.g., "text", "image", "tweet") | -| `content` | string | Raw content if `storage` is null, otherwise storage key/URL | - -### Content Resolution - -**Two-tier resolution**: - -1. **Storage Layer** (optional): Fetch actual content if `storage` is set -2. **Resolver Layer**: Process and transform content for display - -``` -Block - ↓ -storage != null? - ├─ Yes → Storage.getRawContent() - └─ No → Use block.content directly - ↓ -Resolver._getSolvedContent() - ↓ -ContentComponent (Vue) +# Client-Web Info-Base Architecture + +## Purpose + +Describe the peer-local info-base implementation shared by `@inkcre/core` and the browser +applications. Shared product authority lives in the InKCre docs Hub; this document owns +TypeScript models, PostgREST mechanics, resolver/runtime handles, and rendering boundaries. + +## Peer Topology + +```text +PostgreSQL / PostgREST protocol + | + v + Block + Relation models + | + Block.getHydratedContent() + | + exact Resolver + | + safe solved value / browser handle + | + Vue component ``` -### Lifecycle - -Blocks support standard CRUD operations: create, read, update, delete. Relations are cascade-deleted when blocks are removed. - ---- +Client-web is a database-protocol peer, not a thin frontend that delegates every semantic +operation to core-py. Schema/migration authority remains in core-py, while admitted read/write, +storage hydration, resolver interpretation, and rendering execute locally. -## Relation Architecture +## Persisted Graph -### Data Model +### Block -Relations connect blocks with the following key fields: +| Field | Meaning | +| --------------------------- | ----------------------------------------------------- | +| `id` | local graph identity | +| `created_at` / `updated_at` | block-row persistence time | +| `resolver` | exact resolver contract version | +| `storage` | selected storage record, or `null` for inline content | +| `content` | inline string or opaque storage pointer | -| Field | Type | Purpose | -| ------------ | -------- | ----------------------------------- | -| `id` | number | Unique identifier | -| `created_at` | Date | Creation timestamp | -| `source` | BlockRef | Source block (from) | -| `target` | BlockRef | Target block (to) | -| `type` | string | Relation type/semantics (optional) | -| `metadata` | Record | Additional relation data (optional) | +Do not render `block.content` directly unless `storage === null` and the exact resolver contract +uses that inline value. A storage pointer is mechanics, never authored text. -### Directionality +### Relation -Relations are **directed**: `source → target` +Relations are directed graph authority with `from_`, `to_`, and contract-owned `content`. +`includeIn` means the subject block is `to_`; `includeOut` means it is `from_`. These options +filter direct relations and do not request recursive graph traversal. -Query patterns: +Relation payload grammar belongs to its extension/canonical contract. The UI must not invent a +universal relation-type registry or infer ownership from direction alone. -- Outgoing: Relations from a block -- Incoming: Relations to a block -- Bidirectional: All relations involving a block +## Block Hydration -### Relation Types +`Block.getHydratedContent({ refresh = false })` returns: -Relation types are domain-specific: +- `string` when the block is inline; +- `Uint8Array` when a configured storage resolves the opaque pointer. -- `"references"` - Citations or links -- `"childOf"` - Hierarchical parent-child -- `"relatedTo"` - General association -- `"derivedFrom"` - Transformation or derivation - -Extensions can define custom types. - ---- +The private cache key is `(storage, content)`. Cache properties are non-enumerable so Zod/ +transport projections never persist them. A changed pointer misses naturally; `refresh` bypasses +and replaces the current instance snapshot. No cross-instance or cross-peer invalidation is +promised, and storage-backed bytes may change without changing `block.updated_at`. ## Storage System -### Purpose +Storage handlers turn a pointer into bytes. They do not decide MIME, filename, information kind, +or resolver ID. -The **Storage system** abstracts content retrieval, allowing blocks to reference content stored externally rather than inline. +### Generic HTTP -### Architecture - -``` -┌─────────────────────────────────────────┐ -│ Storage (Abstract Base Class) │ -│ - id, type, config │ -│ - getRawContent() → Promise │ -└────────────┬────────────────────────────┘ - │ extends - ┌──────┴──────┬──────────┬──────────┐ - │ │ │ │ -┌─────▼──────┐ ┌───▼───┐ ┌────▼────┐ ┌───▼────┐ -│URLStorage │ │Blob │ │Text │ │Custom │ -│(HTTP) │ │Storage│ │Storage │ │(Ext) │ -└────────────┘ └───────┘ └─────────┘ └────────┘ -``` +`HttpStorage` accepts only HTTP(S), enforces timeout/redirect policy and both declared/received +byte limits, then returns `Uint8Array`. It does not branch on `Content-Type`. -### Storage Registry +### PostgreSQL Binary -**Decorator-based registration** allows extensions to add new storage types. Each storage type implements `getRawContent()` for content retrieval. +`PostgreSQLBinaryStorage` is a complete browser-peer byte capability: -### Key Differences +- create: raw `application/octet-stream` RPC returns UUID and storage-owned pointer JSON; +- read: raw RPC returns `application/octet-stream`; +- update: exact `storage_blobs` UUID row update, pointer remains stable; +- delete: exact UUID row delete. -- **Inline vs External**: Null storage uses `block.content`; set storage delegates to storage handler -- **Type-specific Config**: Each storage type has configurable parameters -- **Error Handling**: Storage layer provides consistent error handling and logging +`storage_blobs` is the backing relation, not a storage type or semantic block. The storage +handler alone parses/serializes `{ "blob_id": "..." }`; callers persist only its opaque string. +Storage C/R/U/D never queries or rewrites blocks. ---- +Authentication and origins come from browser-owned runtime config. `rawPostgrestFetch()` reuses +the current JWT/origin authority but exposes a byte-capable response rather than forcing JSON. ## Resolver System -### Purpose - -**Resolvers** define how block content is processed and displayed. Each resolver type handles a specific content format. +Resolvers interpret hydrated content plus required relations. `Resolver.getClass(id)` is exact; +there is no first/default fallback. Registration is idempotent only for the same class and rejects +collisions. -### Architecture +The shared exact IDs are: +```text +core.text.v1 core.html.v1 core.image.v1 +core.audio.v1 core.video.v1 core.pdf.v1 +core.epub.v1 core.zip.v1 core.file.v1 ``` -┌─────────────────────────────────────────────┐ -│ BaseResolver (Abstract) │ -│ - type, contentComp │ -│ - getSolvedContent() → caching │ -│ - _getSolvedContent() → subclass impl │ -└───────────────┬─────────────────────────────┘ - │ extends - ┌─────────┴─────┬───────────┬────────┐ - │ │ │ │ -┌─────▼─────┐ ┌──────▼───┐ ┌────▼───┐ ┌─▼────────┐ -│Text │ │Image │ │HTML │ │Tweet │ -│Resolver │ │Resolver │ │Resolver│ │(Extension) -└───────────┘ └──────────┘ └────────┘ └──────────┘ -``` - -### Resolver Interface - -Resolvers provide: - -- Content processing and caching -- Lazy relation loading -- Vue component integration -- Loading state management - -### Content Component Pattern - -Content components receive standardized props: - -- `resolver`: Resolver instance -- `solvedContent`: Processed content - -### Resolver Registry - -The `Resolver` base class now hosts the registry itself using `Resolver.register`, `Resolver.registry`, and `Resolver.getClass` so implementations can self-register their `type` and the UI can resolve them by identifier. - -### Resolver Lifecycle - -1. Create resolver instance -2. Fetch and process content (with caching) -3. Display via Vue component -4. Dispose on cleanup - ---- - -## Graph Operations - -### Graph Data Structure -Uses **graphology** library for graph operations, converting blocks to nodes and relations to edges. +Core bootstrap registers all nine independently of Module Federation extension loading. +Extensions register their own namespaced, versioned decoders. -### Community Detection +### Capability Outcomes -**Louvain Algorithm** groups related blocks into communities for visualization and analysis. +Every concrete resolver implements: -### Shortest Path - -Bidirectional search finds shortest paths between blocks. - -### Topology Detection - -Analyzes graph structure to identify: - -- DAG (Directed Acyclic Graph) -- Tree -- Star -- General graphs - -### CRUD Operations - -- **Create**: Add blocks and relations -- **Read**: Query by ID, recent, or relationships -- **Update**: Modify block/relation properties -- **Delete**: Remove with cascade handling - -### Performance - -- Lazy loading prevents unnecessary fetches -- Caching reduces redundant processing -- Batch operations for bulk updates -- Pagination for large datasets - ---- - -## Layout System - -### Layout Manager Architecture - -The **Layout Manager** auto-detects graph topology and selects appropriate algorithms. - -``` -┌───────────────────────────────────────────┐ -│ useLayoutManager │ -│ - Auto-detect topology │ -│ - Select appropriate layout │ -│ - Apply layout and update positions │ -└───────────┬───────────────────────────────┘ - │ uses - ┌───────┴───────┬──────────┬──────────┐ - │ │ │ │ -┌───▼────┐ ┌──────▼───┐ ┌───▼───┐ ┌───▼────┐ -│Force │ │Dagre │ │Circular│ │Radial │ -│Layout │ │(Layered) │ │Layout │ │Layout │ -└────────┘ └──────────┘ └────────┘ └────────┘ +```ts +getText(options?: ProjectionOptions): Promise +getStrForEmbedding(options?: ProjectionOptions): Promise ``` -### Available Layouts +- `UnknownResolverError`: no exact decoder is registered; +- `UnsupportedResolverCapability`: the decoder does not provide that projection; +- `null`: capability exists but the block has no meaningful value; +- `''`: an actual empty value, not a generic fallback. -| Layout | Best For | Description | -| ------------ | -------------------- | -------------------------------- | -| **Force** | General graphs | Physics simulation, organic feel | -| **Dagre** | DAGs, Trees | Hierarchical layered layout | -| **Circular** | Cycles, Small graphs | Nodes arranged in circle | -| **Radial** | Star, Hierarchies | Concentric circles from root | -| **Grid** | Large graphs | Uniform grid arrangement | -| **Auto** | Any | Auto-detect topology and choose | +Do not collapse these outcomes into an empty preview or reinterpret an unknown ID. -### Key Differences +### Effect Vocabulary -- **Force-Directed**: Physics-based, good for organic layouts but computationally intensive -- **Hierarchical (Dagre)**: Layered approach ideal for DAGs and trees -- **Circular/Radial**: Space-efficient for dense or hierarchical graphs -- **Grid**: Predictable positioning for large, uniform datasets +- `refresh`: bypass and replace a local hydration/relation/solved snapshot; +- `materializeMissing`: permit creation only when a required derivation is absent; +- `recompute`: reserved for an explicit organization command that regenerates an existing + derivation; +- `invalidate`: discard cache without loading replacement. -### Vue Flow Integration +Do not add `force` or `reload` aliases for these effects. -Layouts update node positions in Vue Flow graphs, supporting manual and automatic modes. +### Media Matching ---- +`Resolver.matchMediaType()` normalizes one candidate MIME and returns an installed exact core +resolver ID or `null`. Source/protocol adapters own evidence order and explicit +`core.file.v1` fallback; the manager does not own one universal classification ladder. -## Extension Points +## Browser Runtime Handles -### Custom Resolvers +Image/audio/video/PDF/EPUB/ZIP/file resolvers hydrate bytes locally and may expose safe Blob or +Object URL handles. These handles are private runtime state, not solved graph authority, and are +revoked on resolver refresh, disposal, and resolver-cache eviction. -Extensions register resolvers using decorators: +HTML is rendered as a text preview/source action. It is not passed to `v-html` without a separate +sanitizer contract. Parser-derived facts may remain `null` when the browser does not have a +proportionate local parser; open/render/download remains a real capability. -```typescript -@BaseResolver.registry('customType') -export class CustomResolver extends BaseResolver { - // Implement content processing -} -``` +`BlockContent.vue` resolves exact class setup errors visibly, loads solved content, passes it to +the registered component, and disposes the resolver on unmount. Graph/editor surfaces must use +this path rather than falling back to the persisted pointer string. -### Custom Storage +## Resolver Cache -Extensions add storage types: +`ResolverCache` owns reusable resolver instances by block identity/update snapshot. Eviction and +replacement call `dispose()` so object URLs and other handles cannot leak. Resolver-internal solved +content and relation caches still obey `refresh`; cache reuse does not change authority. -```typescript -@Storage.registry('customStorage') -export class CustomStorage extends Storage { - // Implement content retrieval -} -``` +## Extension Producer Rules -### Extension Lifecycle +- Emit exact, versioned resolver IDs. +- Store protocol/source identity and declared metadata in canonical metadata/root blocks. +- Put actual media/document bytes in an exact semantic content block, optionally storage-backed. +- Keep associations in relations rather than copying attachment/reference IDs into root content. +- Never choose a semantic storage type; storage is bytes-only mechanics. -Extensions initialize during startup, registering types that become available system-wide. Deactivation cleans up resources. +Twitter and browser-extension producers have been cut over to these exact semantic IDs. Retired +bare IDs and media-specific HTTP storage handlers are not compatibility fallbacks. ---- +## Verification -**Last Updated**: January 2, 2026 +- `packages/core` tests prove inline/storage hydration, refresh, exact registration, typed + capability outcomes, semantic resolver handles, object URL cleanup, HTTP limits, and PostgreSQL + binary C/R/U/D. +- database-contract generation tests prove admitted relation/function projection. +- repository `pnpm check` proves TypeScript, Vue, unit tests, formatting/lint, and production builds. diff --git a/docs/resolver.md b/docs/resolver.md index 299e24c..ce08a5e 100644 --- a/docs/resolver.md +++ b/docs/resolver.md @@ -1,5 +1,9 @@ -# 解析器 +# Resolver Notes -- 解析器是用于解析不同类型的块内容,提供统一的接口以对块进行展示、修改等操作 -- 块通过其 `resolver` 字段指定解析器 -- 解析器可以通过插件载入 +The durable client-web resolver architecture is owned by +[Client-Web Info-Base Architecture](info-base/ARCHITECTURE.md#resolver-system)。 + +Resolvers are exact, versioned interpreters of hydrated block content and direct relations;they +are not merely display components。Unknown resolver、unsupported capability、supported-null and +authored-empty are distinct outcomes。New code uses `refresh` and `materializeMissing` with the +stable semantics recorded by that architecture document。 diff --git a/extensions/twitter/src/components/contentTweet/contentTweet.vue b/extensions/twitter/src/components/contentTweet/contentTweet.vue index 3436188..6750c31 100644 --- a/extensions/twitter/src/components/contentTweet/contentTweet.vue +++ b/extensions/twitter/src/components/contentTweet/contentTweet.vue @@ -49,7 +49,7 @@ const isAttachmentFailed = (index: number) => {