@@ -417,6 +417,215 @@ Host-backed tools:
417417- ACP Kit can expose client-backed filesystem and shell helpers
418418- projection maps change how tools are rendered in ACP clients without changing the underlying tool contract
419419
420+ ### HostAccessPolicy
421+
422+ ` HostAccessPolicy ` is ACP Kit's native typed guardrail surface for host-backed filesystem and terminal access.
423+
424+ Reach for it first when an integration has started inventing ad hoc rules such as:
425+
426+ - "warn for absolute paths but deny workspace escapes"
427+ - "show one caution in the client but enforce a different rule in the backend"
428+ - "treat command cwd and file paths as unrelated policy domains"
429+
430+ Use it:
431+
432+ - when host-backed file and terminal tools already exist
433+ - when approvals or projection warnings need to describe the same risk model that enforcement uses
434+ - when downstream code has started to accumulate one-off path checks
435+
436+ Do not reach for it:
437+
438+ - when the integration does not expose host-backed file or terminal tools at all
439+ - when the problem is product-specific approval wording rather than reusable access policy
440+
441+ Use it when an integration needs one reusable place to decide:
442+
443+ - whether absolute paths should only warn or hard fail
444+ - whether paths outside the active session cwd should warn or deny
445+ - whether workspace-root escapes should always deny
446+ - whether command cwd and command path arguments should follow the same policy language as file paths
447+
448+ Important distinction:
449+
450+ - ` evaluate_path(...) ` and ` evaluate_command(...) ` are for UI and approval surfaces
451+ - ` enforce_path(...) ` and ` enforce_command(...) ` are for actual blocking before ACP host requests are sent
452+
453+ The evaluation objects are intentionally UI-friendly. They expose:
454+
455+ - ` disposition `
456+ - ` headline `
457+ - ` message `
458+ - ` recommendation `
459+ - ` risks `
460+ - ` risk_codes `
461+ - ` primary_risk `
462+ - ` summary_lines() `
463+
464+ That means downstream integrations do not need to invent their own warning strings just to show a clear caution card.
465+
466+ Typical use:
467+
468+ ``` python
469+ from pydantic_acp import ClientHostContext, HostAccessPolicy
470+
471+ policy = HostAccessPolicy.strict()
472+
473+ host = ClientHostContext.from_session(
474+ client = client,
475+ session = session,
476+ access_policy = policy,
477+ workspace_root = session.cwd,
478+ )
479+ ```
480+
481+ Small verified evaluation example:
482+
483+ ``` python
484+ from pathlib import Path
485+
486+ from pydantic_acp import HostAccessPolicy
487+
488+ policy = HostAccessPolicy.strict()
489+ evaluation = policy.evaluate_path(
490+ ' ../notes.txt' ,
491+ session_cwd = Path(' /workspace/app' ),
492+ workspace_root = Path(' /workspace/app' ),
493+ )
494+
495+ assert evaluation.disposition == ' deny'
496+ assert evaluation.should_deny
497+ assert ' outside_cwd' in evaluation.risk_codes
498+ ```
499+
500+ Current presets:
501+
502+ - ` HostAccessPolicy() ` is conservative default behavior
503+ - ` HostAccessPolicy.strict() ` denies more aggressively outside the active cwd
504+ - ` HostAccessPolicy.permissive() ` keeps more paths executable but still surfaces risk
505+
506+ Current scope:
507+
508+ - file path evaluation
509+ - command cwd evaluation
510+ - heuristic detection of obvious path-like command arguments
511+ - native backend-side deny enforcement
512+
513+ Current limit:
514+
515+ - it is not a full shell parser
516+ - it does not automatically wire itself through every integration seam yet
517+
518+ Primary references:
519+
520+ - [ Host Backends and Projections] ( https://vcoderun.github.io/acpkit/host-backends/ )
521+ - [ Projection Cookbook] ( https://vcoderun.github.io/acpkit/projection-cookbook/ )
522+
523+ ## Black-box Integration Harness
524+
525+ ` BlackBoxHarness ` exists so downstream integrations can prove the ACP boundary without rebuilding test plumbing from scratch.
526+
527+ Reach for it when the integration already "works" but still lacks proof for:
528+
529+ - approval replay
530+ - host-backed side effects
531+ - session reload correctness
532+ - ACP-visible transcript truthfulness
533+
534+ Use it:
535+
536+ - after the integration already has a real adapter construction seam
537+ - when you need one reusable way to drive approvals, prompts, reloads, and visible updates
538+ - when a normal unit test would miss ACP-visible behavior
539+
540+ Do not use it:
541+
542+ - to inspect private helper ordering
543+ - as a substitute for product-level end-to-end testing
544+ - before the integration has a coherent ownership model for sessions, approvals, and host tools
545+
546+ Use it when you want to verify:
547+
548+ - session create/load behavior
549+ - visible ACP updates
550+ - approval roundtrips
551+ - host-backed file or terminal flows
552+ - replay after reload
553+
554+ What it gives you:
555+
556+ - adapter construction plus a recording ACP client in one object
557+ - ` new_session(...) `
558+ - ` load_session(...) `
559+ - ` prompt_text(...) `
560+ - ` set_mode(...) `
561+ - ` set_model(...) `
562+ - permission response queueing helpers
563+ - update filtering
564+ - reconstructed agent messages
565+
566+ Typical use:
567+
568+ ``` python
569+ import asyncio
570+
571+ from pydantic_acp import AdapterConfig, BlackBoxHarness, FileSessionStore
572+
573+ harness = BlackBoxHarness.create(
574+ agent_factory = build_agent,
575+ config = AdapterConfig(session_store = FileSessionStore(tmp_path / ' sessions' )),
576+ )
577+
578+ session = asyncio.run(harness.new_session(cwd = str (tmp_path)))
579+ harness.queue_permission_selected(' allow_once' )
580+ response = asyncio.run(harness.prompt_text(' Write the workspace note.' ))
581+
582+ assert response.stop_reason == ' end_turn'
583+ assert harness.tool_updates(session_id = session.session_id)
584+ assert harness.agent_messages(session_id = session.session_id)
585+ ```
586+
587+ Small verified example from the harness test shape:
588+
589+ ``` python
590+ session = asyncio.run(harness.new_session(cwd = str (tmp_path)))
591+ harness.queue_permission_selected(' allow_once' )
592+ response = asyncio.run(harness.prompt_text(' Write the workspace note.' ))
593+
594+ assert response.stop_reason == ' end_turn'
595+ assert harness.agent_messages(session_id = session.session_id) == [' done' ]
596+ ```
597+
598+ The harness is intentionally black-box.
599+
600+ Prefer asserting on:
601+
602+ - ACP return values
603+ - emitted ` ToolCallStart ` / ` ToolCallProgress ` updates
604+ - reconstructed visible messages
605+ - persisted replay behavior
606+ - real host-backed side effects
607+
608+ Do not use it to:
609+
610+ - inspect private helper choreography
611+ - lock internal runtime call order
612+ - replace product-level end-to-end tests
613+
614+ Good default scenario ladder for a new integration:
615+
616+ 1 . session create -> prompt -> reload
617+ 2 . approval required -> allow once
618+ 3 . approval required -> deny once
619+ 4 . host-backed file read/write
620+ 5 . host-backed terminal execution
621+ 6 . mode switch changes behavior
622+ 7 . model switch changes session-local state
623+
624+ Primary references:
625+
626+ - [ Integration Testing] ( https://vcoderun.github.io/acpkit/integration-testing/ )
627+ - [ Examples Overview] ( https://vcoderun.github.io/acpkit/examples/ )
628+
420629## Projection Maps And Hook Rendering
421630
422631Projection maps make ACP clients see richer file or command behavior than a raw generic tool card.
@@ -430,6 +639,30 @@ Common maps:
430639Use them when the client should see more structured output, but avoid pretending a tool is
431640something it is not.
432641
642+ ### Projection Helper Primitives
643+
644+ ACP Kit now also ships small reusable projection helpers for integrations that need consistent shaping but do not want to rebuild truncation and warning logic repeatedly.
645+
646+ High-value helpers:
647+
648+ - ` truncate_text(...) `
649+ - ` truncate_lines(...) `
650+ - ` single_line_summary(...) `
651+ - ` format_code_block(...) `
652+ - ` format_diff_preview(...) `
653+ - ` format_terminal_status(...) `
654+ - ` caution_for_path(...) `
655+ - ` caution_for_command(...) `
656+
657+ Use these helpers when:
658+
659+ - a chat client needs a plain-text diff preview
660+ - command titles should stay compact and consistent
661+ - long stdout/stderr content needs predictable truncation
662+ - caution text should come from the same ` HostAccessPolicy ` evaluation model as backend enforcement
663+
664+ These helpers are intentionally small. They are building blocks, not a full rendering framework.
665+
433666## Examples That Matter
434667
435668High-value maintained examples live under ` examples/pydantic/ ` :
@@ -470,10 +703,101 @@ High-value docs pages:
470703- [ Providers] ( https://vcoderun.github.io/acpkit/providers/ )
471704- [ Bridges] ( https://vcoderun.github.io/acpkit/bridges/ )
472705- [ Host Backends and Projections] ( https://vcoderun.github.io/acpkit/host-backends/ )
706+ - [ Integration Testing] ( https://vcoderun.github.io/acpkit/integration-testing/ )
707+ - [ Projection Cookbook] ( https://vcoderun.github.io/acpkit/projection-cookbook/ )
473708- [ Examples Overview] ( https://vcoderun.github.io/acpkit/examples/ )
474709- [ Workspace Agent] ( https://vcoderun.github.io/acpkit/examples/workspace-agent/ )
475710- [ pydantic_acp API] ( https://vcoderun.github.io/acpkit/api/pydantic_acp/ )
476711
712+ ## Compatibility Manifest
713+
714+ ACP Kit now also ships a typed root-level compatibility manifest schema through ` acpkit ` .
715+
716+ Reach for it after the integration already has real seams and at least one black-box proof path.
717+
718+ Do not use it as a speculative roadmap scratchpad. Use it as a reviewable declaration of what is actually wired today.
719+
720+ Use it:
721+
722+ - after at least one black-box proof path exists
723+ - when reviews need one typed declaration of supported ACP surfaces
724+ - when docs should be generated from validated code instead of prose drift
725+
726+ Do not use it:
727+
728+ - before the integration can already demonstrate the behavior it claims
729+ - as a replacement for proof tests
730+ - as a vague backlog matrix with no mapping seam
731+
732+ Use it when a real integration needs one reviewable declaration of:
733+
734+ - which ACP surfaces are implemented
735+ - which are partial
736+ - which are intentionally not used
737+ - which are only planned
738+
739+ Core types:
740+
741+ - ` CompatibilityManifest `
742+ - ` SurfaceSupport `
743+ - ` SurfaceStatus `
744+ - ` SurfaceOwner `
745+
746+ Typical use:
747+
748+ ``` python
749+ from acpkit import CompatibilityManifest, SurfaceSupport
750+
751+ manifest = CompatibilityManifest(
752+ integration_name = ' workspace-agent' ,
753+ adapter = ' pydantic-acp' ,
754+ surfaces = {
755+ ' session.load' : SurfaceSupport(
756+ status = ' implemented' ,
757+ owner = ' adapter' ,
758+ mapping = ' FileSessionStore + load_session' ,
759+ ),
760+ ' mode.switch' : SurfaceSupport(
761+ status = ' partial' ,
762+ owner = ' bridge' ,
763+ mapping = ' PrepareToolsBridge dynamic modes' ,
764+ rationale = ' Only explicitly exposed runtime modes are surfaced.' ,
765+ ),
766+ ' authenticate' : SurfaceSupport(
767+ status = ' planned' ,
768+ rationale = ' No auth handshake has been added yet.' ,
769+ ),
770+ },
771+ )
772+
773+ manifest.validate()
774+ ```
775+
776+ Minimal review rule:
777+
778+ - every ` implemented ` surface should point to one concrete mapping seam
779+ - every ` partial ` , ` intentionally_not_used ` , and ` planned ` surface should explain why
780+ - ` mixed ` ownership is only acceptable when the split is named explicitly
781+
782+ Important rule:
783+
784+ - do not generate this from guesses
785+ - derive it from a real integration audit
786+ - then validate it in tests
787+
788+ Recommended workflow:
789+
790+ 1 . inventory real seams
791+ 2 . declare surfaces in code
792+ 3 . call ` manifest.validate() ` in tests or CI
793+ 4 . optionally publish ` manifest.to_markdown() ` into docs
794+
795+ This is not a runtime feature. It is an integration review and documentation hygiene feature.
796+
797+ Primary reference:
798+
799+ - [ Compatibility Manifest Guide] ( https://vcoderun.github.io/acpkit/compatibility-matrix-template/ )
800+
477801## Skill-Local Routing Aids
478802
479803These files exist to route you quickly into the right part of the codebase or docs set when the
@@ -499,6 +823,7 @@ Use the bundled scripts instead of guessing:
499823- Keep examples runnable, explicit, and strongly typed.
500824- Treat adapter-owned state and host-owned state as different design choices.
501825- Prefer the narrowest seam that matches the user’s need.
826+ - Use the compatibility manifest when an integration needs one typed, reviewable ACP surface declaration instead of a loose prose matrix.
502827- ` FileSessionStore ` uses ` root=Path(...) ` .
503828- ` FileSessionStore ` is the hardened local durable store, not a distributed session backend.
504829- Mode slash commands are dynamic, and mode ids must not collide with reserved names such as ` model ` , ` thinking ` , ` tools ` , ` hooks ` , or ` mcp-servers ` .
0 commit comments