跳到內容
Crystal CodeDocs
繁繁體中文

Standards

本頁內容尚未翻譯。

  • Prefer a small explicit surface over convenience APIs with hidden behavior.
  • Keep BUSINESS.md, ARCHITECTURE.md, and this file synchronized with material behavior changes.
  • Runtime and exception text is plain English and contains no emoji.
  • Model-visible tool text uses explicit LF separators (ToolOutputText.LineSeparator, ToolOutputText.AppendLine), never Environment.NewLine or StringBuilder.AppendLine. Tool text reaches providers and session files, so it stays identical on every platform.
  • Crystal-authored diagnostics rules still apply to host logs: no credentials, no raw API keys, no secret file contents.
  • Comments explain constraints and intent rather than syntax.
  • Do not leave commented-out production code.
  • Do not modify dependencies without explicit user authorization.
  • File-scoped namespaces.
  • Exactly one top-level type per file. File name equals type name.
  • Folder path under the project root equals the namespace after the project name.
  • Using directives: System, then third-party, then Crystal, then CrystalCode, with a blank line between groups that are present.
  • No top-level statements.
  • Solution Explorer layout follows Visual Studio / Rider: .sln at the repository root, project folders beside it, tests as {Project}.Tests siblings (CrystalCode.Tests, CrystalCode.Engine.Tests, CrystalCode.Display.Tests, CrystalCode.Providers.Tests). No src/ or root tests/ tree.
  • Engine code never calls Console, never draws, and never reads keys. Report through a SessionEvent or a SessionFrontEnd contract. New events are sealed immutable records, one per file under CrystalCode.Engine/Events, and carry engine values rather than display captions.
  • Do not widen InternalsVisibleTo to give a front end engine internals. Make the member public when a second front end could need it, otherwise keep it out of the front end.
  • PascalCase for types and public members, camelCase for locals and parameters, _camelCase for private fields.
  • Prefix interfaces with I.
  • Use C# keywords (string, int) instead of CLR type names.
  • Braces on every control-flow block.
  • Every switch has an explicit fallback.
  • Immutable records for value data. Classes for stateful behavior.
  • Collection expressions when they improve clarity.
  • nameof for parameter and code-element references.
  • Avoid magic numbers, double negatives, nested loops, and clever compression.
  • Nullable reference types stay enabled.
  • Declare variables near first use, one variable per declaration.
  • External or I/O operations are asynchronous.
  • Async methods end in Async.
  • CancellationToken is the last parameter and is propagated.
  • Streams return IAsyncEnumerable<T>.
  • Never call Result, Wait, or GetAwaiter().GetResult().
  • Library-style code in Providers uses ConfigureAwait(false).
  • Application code in the executable host and in CrystalCode.Engine does not need ConfigureAwait. A front end awaits engine calls and never blocks on them.
  • Do not create unobserved background work.
  • Write, edit, read, list, tree, glob, grep, and search of paths outside the workspace require approval unless session directory access covers the action. Session read permits file reads; write includes reads and retains normal action approval. Explicit read-only writes are rejected before grants, raw approval advice, or review and checked again by the built-in write/edit path resolver. Rules are saved with the session, not config.json or permissions.json. Resume restores those saved rules and does not reapply defaults. A new session, clear, fork, and a successful workspace change seed ~/.crystal/access.json for that canonical workspace root unless --allow-read or --allow-write was passed; those flags replace the defaults for that process and are not written back. A refused or no-op workspace change keeps the current rules. Canonical directory boundaries and most-specific matching include the implicit writable workspace. Persist the lexical absolute entry path when it differs from the canonical target, for revocation after symlink deletion or retargeting; never use that alias for authorization or re-resolve it on resume. Validate saved access before returning a resumable document or allocating a session. Invalid access must produce a visible error, not crash interactive startup or replace the current session. Search descriptions and path schemas must agree that roots may be workspace-relative or absolute and only the requested root is searched. Unlisted outside paths retain approval: in Review or Audit the reviewing model judges them; otherwise the operator is asked. Edit, Review, and Full do not auto-pass unlisted outside writes or edits. These rules do not sandbox shell, background shell, plugins, or external processes. External dotnet tools may supply a side-effect-free pre-classifier when their actual write paths are embedded in arguments; it must fail closed, it can only raise risk or authority for the call, and it never replaces the Write + Workspace floor, the declared path checks, or the source trust policy. Its asynchronous entry receives cancellation and a separate two-second budget. Timeout/cancellation disables the classifier until reload; late tasks stay observed. Invalid fields, exceptions, and timeouts fail the proposal closed on both classification axes. Final summaries are host-authored. Reusable grants only apply to Workspace Read/Write without a forced prompt; forced prompts also prevent reviewer Allow from executing without the operator. Path checks follow symbolic links to their final target. A link that leaves the workspace is outside the workspace.
  • When Skills is enabled, any path inside a Skills search directory (skill / skills trees) auto-passes as a workspace read. Credential paths stay Forbidden, including workspace-relative .ssh, .gnupg, and .crystal/credentials.json. Classification and execution both reject them, and workspace-wide search skips those files.
  • Shell classification treats sudo, destructive filesystem commands, pipe-to-shell downloads, force-push, and credential-path writes as Forbidden or Privileged. Forbidden never fully auto-passes. Review and Audit may deny those calls or escalate them to the operator.
  • crystal run has no operator. Review and Audit still ask the reviewing model. Any call that would ask the operator is denied with the existing rejection text. The question tool is dismissed. Credential paths stay Forbidden. Flags on that command are not written to config.json. Do not put secrets on the command line.
  • Credential files are written with owner-only access where the OS allows it.
  • New provider catalog files are created with owner-only access because provider definitions may contain credential references or inline keys.
  • Do not log request bodies that may contain secrets.
  • Do not put image bytes, data URIs, or remote image URIs in composer text, transcript rendering, logs, diagnostics, or exception messages. Use stable [Image #N] markers.
  • Treat attachment provenance as separate from marker spelling. User-entered text that resembles an image marker is plain text, not a media reference.
  • Prompt history persists text-only entries under ~/.crystal with owner-only permissions where supported. Do not persist image markers without their session attachment identity.
  • Validate local image content by signature, enforce the host size limit, and preserve exact bytes and MIME type. Do not decode, transform, fetch, or execute media implicitly.
  • Validate size and MIME type again before copying session images to the owner-only media store. Persist and verify a content hash; write the media file before its session reference. Missing or damaged media must not make the remaining session unreadable.
  • Keep text and image-capable turn budgets on the same configured limits. A null limit is unlimited; omitted budget fields retain the finite defaults.
  • Built-in bash uses the same rule for bashTimeoutSeconds: omit it to keep 600 seconds; null or "unlimited" removes the per-command timer.
  • Background Bash uses the independent backgroundBashTimeoutSeconds timer: omit it to keep 10800 seconds (3 hours); null or "unlimited" removes that timer.
  • Background Bash tasks are session-local and must remain observable through their status or bounded logs. They are not persisted, and session shutdown stops their process trees. A model must not create an unobserved background task.
  • Extension runtime data lives under data/{plugins,tools}/<directory>/ in the home and project .crystal trees. The host creates it when the extension first receives it and it is separate from the install tree. It is not a secret store: do not persist credentials there, and the project directory is workspace content.

Authorized packages today:

  • Spectre.Console in CrystalCode.Display (session rasterization) and CrystalCode (host cards that still build Spectre widgets). Never in CrystalCode.Engine.
  • Spectre.Console.Cli in CrystalCode.
  • Terminal.Gui in CrystalCode.Display only, with a floating version (*). It is parked for supply-chain review. Production and test code must not import Terminal.Gui types. Do not add a dummy using to silence unused-package warnings; document a restore suppression if NU1510 is raised.
  • Newtonsoft.Json, Newtonsoft.Json.Bson, and System.Text.Json in CrystalCode.Engine and CrystalCode.Providers, where the Crystal sibling already requires them for project-reference consistency.
  • xUnit and Microsoft.NET.Test.Sdk in CrystalCode.Tests, CrystalCode.Engine.Tests, CrystalCode.Display.Tests, and CrystalCode.Providers.Tests.

Do not add another package without asking.

Terminal window
dotnet build CrystalCode.sln
dotnet test CrystalCode.sln

Tests run on Windows, macOS, and Linux (the release workflow runs the suite on each), so they stay portable. Do not hardcode a single-platform path such as /tmp or a Windows drive root in a test that touches the real filesystem; derive it from Path.GetTempPath(), Path.Combine, or an existing temporary-directory helper. A test that only compares path strings and never reads or writes the disk is not constrained.

Do not claim coverage a test project does not actually exercise. Session, approval, compaction, storage, and tool behavior is tested in CrystalCode.Engine.Tests, including a headless session driven by a scripted model through ISessionObserver. Terminal projection, renderer, and prompt surfaces are tested in CrystalCode.Tests. crystal run is tested there with a scripted model for a completed reply, a review allow that executes a tool, and an operator prompt that is denied with a non-zero exit. Frame, composer, and paint behavior is tested in CrystalCode.Display.Tests. Adapters are tested in CrystalCode.Providers.Tests. EngineAssemblyTests and DisplayAssemblyTests guard the dependency direction and must not be weakened to make a change pass.